Repository: ServiceComb/ServiceComb-Java-Chassis Branch: master Commit: ed86d043e894 Files: 3326 Total size: 9.1 MB Directory structure: gitextract_c_qmn9s_/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation_issue.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── checkstyle.yml │ ├── linelint.yml │ ├── maven.yml │ ├── rat_check.yml │ └── typo_check.yml ├── .gitignore ├── .typos.toml ├── LICENSE ├── NOTICE ├── README.md ├── README_ZH.md ├── ci/ │ ├── README.md │ ├── checkstyle/ │ │ ├── checkstyle.xml │ │ └── suppressions.xml │ └── spotbugs/ │ └── exclude.xml ├── clients/ │ ├── README.md │ ├── config-center-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── config/ │ │ └── center/ │ │ └── client/ │ │ ├── ConfigCenterAddressManager.java │ │ ├── ConfigCenterClient.java │ │ ├── ConfigCenterConfigurationChangedEvent.java │ │ ├── ConfigCenterManager.java │ │ ├── ConfigCenterOperation.java │ │ └── model/ │ │ ├── ConfigCenterConfiguration.java │ │ ├── QueryConfigurationsRequest.java │ │ └── QueryConfigurationsResponse.java │ ├── config-common/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── config/ │ │ └── common/ │ │ ├── ConfigConverter.java │ │ └── exception/ │ │ └── OperationException.java │ ├── config-kie-client/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ └── kie/ │ │ │ └── client/ │ │ │ ├── KieClient.java │ │ │ ├── KieConfigManager.java │ │ │ ├── KieConfigOperation.java │ │ │ ├── KieConfigurationChangedEvent.java │ │ │ └── model/ │ │ │ ├── ConfigConstants.java │ │ │ ├── ConfigurationsRequest.java │ │ │ ├── ConfigurationsRequestFactory.java │ │ │ ├── ConfigurationsResponse.java │ │ │ ├── KVDoc.java │ │ │ ├── KVResponse.java │ │ │ ├── KieAddressManager.java │ │ │ ├── KieConfiguration.java │ │ │ ├── LabelDocResponse.java │ │ │ └── ValueType.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── config/ │ │ └── kie/ │ │ └── client/ │ │ └── model/ │ │ ├── ConfigurationsRequestTest.java │ │ └── KieAddressManagerTest.java │ ├── dashboard-client/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── dashboard/ │ │ │ └── client/ │ │ │ ├── DashboardAddressManager.java │ │ │ ├── DashboardClient.java │ │ │ ├── DashboardOperation.java │ │ │ └── model/ │ │ │ ├── InterfaceInfo.java │ │ │ └── MonitorData.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── dashboard/ │ │ └── client/ │ │ └── AddressManagerTest.java │ ├── http-client-common/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── http/ │ │ │ └── client/ │ │ │ ├── auth/ │ │ │ │ ├── DefaultRequestAuthHeaderProvider.java │ │ │ │ └── RequestAuthHeaderProvider.java │ │ │ ├── common/ │ │ │ │ ├── AbstractAddressManager.java │ │ │ │ ├── HttpConfiguration.java │ │ │ │ ├── HttpRequest.java │ │ │ │ ├── HttpResponse.java │ │ │ │ ├── HttpTransport.java │ │ │ │ ├── HttpTransportFactory.java │ │ │ │ ├── HttpTransportImpl.java │ │ │ │ ├── HttpUtils.java │ │ │ │ ├── MessageObjectMapper.java │ │ │ │ ├── SSLSocketFactoryExt.java │ │ │ │ ├── URLEndPoint.java │ │ │ │ ├── WebSocketListener.java │ │ │ │ └── WebSocketTransport.java │ │ │ ├── event/ │ │ │ │ ├── EngineConnectChangedEvent.java │ │ │ │ ├── OperationEvents.java │ │ │ │ └── RefreshEndpointEvent.java │ │ │ ├── task/ │ │ │ │ ├── AbstractTask.java │ │ │ │ └── Task.java │ │ │ └── utils/ │ │ │ └── ServiceCombServiceAvailableUtils.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── http/ │ │ │ └── client/ │ │ │ └── common/ │ │ │ ├── AbstractAddressManagerTest.java │ │ │ └── HttpTransportImplTest.java │ │ └── resources/ │ │ └── tls/ │ │ ├── client.p12 │ │ └── server.jks │ ├── pom.xml │ └── service-center-client/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── service/ │ │ └── center/ │ │ └── client/ │ │ ├── CacheableServiceCenterOperation.java │ │ ├── DiscoveryEvents.java │ │ ├── RegistrationEvents.java │ │ ├── ServiceCenterAddressManager.java │ │ ├── ServiceCenterClient.java │ │ ├── ServiceCenterDiscovery.java │ │ ├── ServiceCenterOperation.java │ │ ├── ServiceCenterRawClient.java │ │ ├── ServiceCenterRegistration.java │ │ ├── ServiceCenterWatch.java │ │ ├── exception/ │ │ │ └── OperationException.java │ │ └── model/ │ │ ├── BasePath.java │ │ ├── CreateMicroserviceInstanceRequest.java │ │ ├── CreateMicroserviceRequest.java │ │ ├── CreateSchemaRequest.java │ │ ├── DataCenterInfo.java │ │ ├── ErrorMessage.java │ │ ├── FindMicroserviceInstancesResponse.java │ │ ├── Framework.java │ │ ├── GetSchemaListResponse.java │ │ ├── GetSchemaResponse.java │ │ ├── HealthCheck.java │ │ ├── HealthCheckMode.java │ │ ├── HeartbeatsRequest.java │ │ ├── InstancesRequest.java │ │ ├── Microservice.java │ │ ├── MicroserviceInstance.java │ │ ├── MicroserviceInstanceResponse.java │ │ ├── MicroserviceInstanceStatus.java │ │ ├── MicroserviceInstancesResponse.java │ │ ├── MicroserviceResponse.java │ │ ├── MicroserviceStatus.java │ │ ├── MicroservicesResponse.java │ │ ├── ModifySchemasRequest.java │ │ ├── RbacTokenRequest.java │ │ ├── RbacTokenResponse.java │ │ ├── RegisteredMicroserviceInstanceResponse.java │ │ ├── RegisteredMicroserviceResponse.java │ │ ├── SchemaInfo.java │ │ ├── ServiceCenterConfiguration.java │ │ └── UpdatePropertiesRequest.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── service/ │ │ └── center/ │ │ └── client/ │ │ ├── ServiceCenterAddressManagerTest.java │ │ ├── ServiceCenterClientTest.java │ │ └── ServiceCenterRawClientTest.java │ └── resources/ │ └── tls/ │ ├── client.p12 │ └── server.jks ├── common/ │ ├── common-access-log/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── common/ │ │ │ │ └── accessLog/ │ │ │ │ ├── AccessLogBootListener.java │ │ │ │ ├── AccessLogBootstrap.java │ │ │ │ ├── AccessLogConfig.java │ │ │ │ ├── AccessLogConfiguration.java │ │ │ │ ├── AccessLogInitializer.java │ │ │ │ ├── client/ │ │ │ │ │ └── ClientDefaultInitializer.java │ │ │ │ ├── core/ │ │ │ │ │ ├── AccessLogGenerator.java │ │ │ │ │ ├── element/ │ │ │ │ │ │ ├── AccessLogItem.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ConfigurableDatetimeAccessItem.java │ │ │ │ │ │ ├── CookieAccessItem.java │ │ │ │ │ │ ├── DurationMillisecondAccessItem.java │ │ │ │ │ │ ├── DurationSecondAccessItem.java │ │ │ │ │ │ ├── FirstLineOfRequestAccessItem.java │ │ │ │ │ │ ├── HttpMethodAccessItem.java │ │ │ │ │ │ ├── HttpStatusAccessItem.java │ │ │ │ │ │ ├── InvocationContextAccessItem.java │ │ │ │ │ │ ├── LocalHostAccessItem.java │ │ │ │ │ │ ├── LocalPortAccessItem.java │ │ │ │ │ │ ├── PlainTextAccessItem.java │ │ │ │ │ │ ├── QueryStringAccessItem.java │ │ │ │ │ │ ├── RemoteHostAccessItem.java │ │ │ │ │ │ ├── RequestHeaderAccessItem.java │ │ │ │ │ │ ├── RequestProtocolAccessItem.java │ │ │ │ │ │ ├── ResponseHeaderAccessItem.java │ │ │ │ │ │ ├── ResponseSizeAccessItem.java │ │ │ │ │ │ ├── TraceIdAccessItem.java │ │ │ │ │ │ ├── TransportAccessItem.java │ │ │ │ │ │ ├── UrlPathAccessItem.java │ │ │ │ │ │ └── UrlPathWithQueryAccessItem.java │ │ │ │ │ ├── parser/ │ │ │ │ │ │ ├── AccessLogItemCreator.java │ │ │ │ │ │ ├── AccessLogItemMeta.java │ │ │ │ │ │ ├── AccessLogPatternParser.java │ │ │ │ │ │ ├── CompositeVertxRestAccessLogItemMeta.java │ │ │ │ │ │ ├── VertxRestAccessLogItemMeta.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── DefaultCompositeVertxRestAccessLogItemMeta.java │ │ │ │ │ │ └── VertxRestAccessLogPatternParser.java │ │ │ │ │ └── placeholder/ │ │ │ │ │ └── AccessLogItemTypeEnum.java │ │ │ │ └── server/ │ │ │ │ └── ServerDefaultInitializer.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── org.apache.servicecomb.common.accessLog.AccessLogInitializer │ │ │ │ └── org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── common/ │ │ │ └── accessLog/ │ │ │ └── core/ │ │ │ ├── AccessLogGeneratorTest.java │ │ │ ├── element/ │ │ │ │ └── impl/ │ │ │ │ ├── CookieItemTest.java │ │ │ │ ├── DatetimeConfigurableItemTest.java │ │ │ │ ├── DurationMillisecondItemTest.java │ │ │ │ ├── DurationSecondItemTest.java │ │ │ │ ├── FirstLineOfRequestItemTest.java │ │ │ │ ├── HttpMethodItemTest.java │ │ │ │ ├── HttpStatusItemTest.java │ │ │ │ ├── InvocationContextItemTest.java │ │ │ │ ├── LocalHostItemTest.java │ │ │ │ ├── LocalPortItemTest.java │ │ │ │ ├── PlainTextItemTest.java │ │ │ │ ├── QueryStringItemTest.java │ │ │ │ ├── RemoteHostItemTest.java │ │ │ │ ├── RequestHeaderItemTest.java │ │ │ │ ├── RequestProtocolItemTest.java │ │ │ │ ├── ResponseHeaderItemTest.java │ │ │ │ ├── ResponseSizeItemTest.java │ │ │ │ ├── TraceIdItemTest.java │ │ │ │ ├── TransportItemTest.java │ │ │ │ ├── UrlPathItemTest.java │ │ │ │ ├── UrlPathWithQueryItemTest.java │ │ │ │ ├── UserDefinedAccessLogItem.java │ │ │ │ └── UserDefinedAccessLogItemLowPriority.java │ │ │ └── parser/ │ │ │ └── impl/ │ │ │ ├── TestCompositeExtendedAccessLogItemMeta.java │ │ │ ├── TestSingleExtendedAccessLogItemMeta.java │ │ │ └── VertxRestAccessLogPatternParserTest.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta │ ├── common-protobuf/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── codec/ │ │ │ └── protobuf/ │ │ │ ├── definition/ │ │ │ │ ├── HighwayJsonUtils.java │ │ │ │ ├── OperationProtobuf.java │ │ │ │ ├── ProtobufManager.java │ │ │ │ ├── RequestRootDeserializer.java │ │ │ │ ├── RequestRootSerializer.java │ │ │ │ ├── ResponseRootDeserializer.java │ │ │ │ └── ResponseRootSerializer.java │ │ │ ├── internal/ │ │ │ │ └── converter/ │ │ │ │ ├── ProtoMethod.java │ │ │ │ ├── ProtoResponse.java │ │ │ │ ├── ProtoToStringGenerator.java │ │ │ │ ├── SchemaSwaggerTypeAdapter.java │ │ │ │ ├── SwaggerToProtoGenerator.java │ │ │ │ └── SwaggerTypeAdapter.java │ │ │ ├── schema/ │ │ │ │ └── SchemaToProtoGenerator.java │ │ │ └── utils/ │ │ │ └── ScopedProtobufSchemaManager.java │ │ └── test/ │ │ ├── java/ │ │ │ ├── io/ │ │ │ │ └── protostuff/ │ │ │ │ └── runtime/ │ │ │ │ └── model/ │ │ │ │ ├── ModelProtostuff.java │ │ │ │ └── User.java │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── codec/ │ │ │ └── protobuf/ │ │ │ ├── definition/ │ │ │ │ └── TestResponseRootDeserializer.java │ │ │ ├── internal/ │ │ │ │ └── converter/ │ │ │ │ ├── TestProtoToStringGenerator.java │ │ │ │ ├── TestSchemaMetaCodec.java │ │ │ │ ├── TestSchemaMetaCodecRestTemplate.java │ │ │ │ ├── TestSwaggerToProtoGenerator.java │ │ │ │ └── model/ │ │ │ │ ├── FieldNeedWrap.java │ │ │ │ ├── ProtoSchema.java │ │ │ │ ├── ProtoSchemaIntf.java │ │ │ │ ├── ProtoSchemaPojo.java │ │ │ │ ├── Ref1.java │ │ │ │ └── Ref2.java │ │ │ └── schema/ │ │ │ ├── TestSchemaCodec.java │ │ │ ├── TestSchemaToProtoGenerator.java │ │ │ └── model/ │ │ │ ├── CyclicInfo.java │ │ │ ├── DeptInfo.java │ │ │ ├── SchemaService.java │ │ │ ├── ScoreInfo.java │ │ │ └── UserInfo.java │ │ ├── proto/ │ │ │ └── ModelProtobuf.proto │ │ └── resources/ │ │ └── ProtoSchema.proto │ ├── common-rest/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── common/ │ │ │ │ └── rest/ │ │ │ │ ├── CommonRestConfiguration.java │ │ │ │ ├── EdgeServerWebSocketInvocationCreator.java │ │ │ │ ├── HttpTransportContext.java │ │ │ │ ├── ProviderServerWebSocketInvocationCreator.java │ │ │ │ ├── RestConst.java │ │ │ │ ├── RestEngineSchemaListener.java │ │ │ │ ├── RestProducerInvocationCreator.java │ │ │ │ ├── RestProducerInvocationFlow.java │ │ │ │ ├── RestVertxProducerInvocationCreator.java │ │ │ │ ├── ServerWebSocketInvocationCreator.java │ │ │ │ ├── UploadConfig.java │ │ │ │ ├── VertxHttpTransportContext.java │ │ │ │ ├── WebSocketTransportContext.java │ │ │ │ ├── codec/ │ │ │ │ │ ├── RestClientRequest.java │ │ │ │ │ ├── RestCodec.java │ │ │ │ │ ├── RestObjectMapperFactory.java │ │ │ │ │ ├── header/ │ │ │ │ │ │ ├── HeaderCodec.java │ │ │ │ │ │ ├── HeaderCodecCsv.java │ │ │ │ │ │ ├── HeaderCodecMulti.java │ │ │ │ │ │ ├── HeaderCodecPipes.java │ │ │ │ │ │ ├── HeaderCodecSimple.java │ │ │ │ │ │ ├── HeaderCodecSsv.java │ │ │ │ │ │ ├── HeaderCodecWithDelimiter.java │ │ │ │ │ │ └── HeaderCodecsUtils.java │ │ │ │ │ ├── param/ │ │ │ │ │ │ ├── AbstractParamProcessor.java │ │ │ │ │ │ ├── BodyProcessorCreator.java │ │ │ │ │ │ ├── CookieProcessorCreator.java │ │ │ │ │ │ ├── FormProcessorCreator.java │ │ │ │ │ │ ├── HeaderProcessorCreator.java │ │ │ │ │ │ ├── ParamValueProcessor.java │ │ │ │ │ │ ├── ParamValueProcessorCreator.java │ │ │ │ │ │ ├── ParamValueProcessorCreatorManager.java │ │ │ │ │ │ ├── PathProcessorCreator.java │ │ │ │ │ │ └── QueryProcessorCreator.java │ │ │ │ │ ├── produce/ │ │ │ │ │ │ ├── ProduceEventStreamProcessor.java │ │ │ │ │ │ ├── ProduceJsonProcessor.java │ │ │ │ │ │ ├── ProduceProcessor.java │ │ │ │ │ │ ├── ProduceProcessorManager.java │ │ │ │ │ │ ├── ProduceProtoBufferProcessor.java │ │ │ │ │ │ └── ProduceTextPlainProcessor.java │ │ │ │ │ └── query/ │ │ │ │ │ ├── AbstractQueryCodec.java │ │ │ │ │ ├── QueryCodec.java │ │ │ │ │ ├── QueryCodecCsv.java │ │ │ │ │ ├── QueryCodecMulti.java │ │ │ │ │ ├── QueryCodecPipes.java │ │ │ │ │ ├── QueryCodecSsv.java │ │ │ │ │ ├── QueryCodecWithDelimiter.java │ │ │ │ │ ├── QueryCodecs.java │ │ │ │ │ └── QueryCodecsUtils.java │ │ │ │ ├── definition/ │ │ │ │ │ ├── RestMetaUtils.java │ │ │ │ │ ├── RestOperationComparator.java │ │ │ │ │ ├── RestOperationMeta.java │ │ │ │ │ ├── RestParam.java │ │ │ │ │ └── path/ │ │ │ │ │ ├── AbstractUrlParamWriter.java │ │ │ │ │ ├── PathRegExp.java │ │ │ │ │ ├── PathVarParamWriter.java │ │ │ │ │ ├── QueryVarParamWriter.java │ │ │ │ │ ├── StaticUrlParamWriter.java │ │ │ │ │ ├── URLPathBuilder.java │ │ │ │ │ └── UrlParamWriter.java │ │ │ │ ├── filter/ │ │ │ │ │ └── inner/ │ │ │ │ │ ├── RestServerCodecFilter.java │ │ │ │ │ └── WebSocketServerCodecFilter.java │ │ │ │ ├── locator/ │ │ │ │ │ ├── MicroservicePaths.java │ │ │ │ │ ├── OperationGroup.java │ │ │ │ │ ├── OperationLocator.java │ │ │ │ │ └── ServicePathManager.java │ │ │ │ ├── resource/ │ │ │ │ │ ├── ClassPathStaticResourceHandler.java │ │ │ │ │ └── StaticResourceHandler.java │ │ │ │ └── route/ │ │ │ │ ├── URLMappedConfigurationItem.java │ │ │ │ ├── URLMappedConfigurationLoader.java │ │ │ │ └── Utils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── com.fasterxml.jackson.databind.Module │ │ │ │ ├── org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor │ │ │ │ └── org.apache.servicecomb.core.filter.FilterProvider │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── common/ │ │ │ └── rest/ │ │ │ ├── RestProducerInvocationCreatorTest.java │ │ │ ├── TestDefPath.java │ │ │ ├── TestRestEngineSchemaListener.java │ │ │ ├── codec/ │ │ │ │ ├── PojoModel.java │ │ │ │ ├── TestRestCodec.java │ │ │ │ ├── TestRestObjectMapper.java │ │ │ │ ├── fix/ │ │ │ │ │ └── TestDoSFix.java │ │ │ │ ├── param/ │ │ │ │ │ ├── TestBodyProcessor.java │ │ │ │ │ ├── TestBodyProcessorCreator.java │ │ │ │ │ ├── TestCookieProcessor.java │ │ │ │ │ ├── TestCookieProcessorCreator.java │ │ │ │ │ ├── TestFormProcessor.java │ │ │ │ │ ├── TestFormProcessorCreator.java │ │ │ │ │ ├── TestHeaderProcessor.java │ │ │ │ │ ├── TestHeaderProcessorCreator.java │ │ │ │ │ ├── TestPathProcessor.java │ │ │ │ │ ├── TestPathProcessorCreator.java │ │ │ │ │ ├── TestQueryProcessor.java │ │ │ │ │ └── TestQueryProcessorCreator.java │ │ │ │ ├── produce/ │ │ │ │ │ ├── TestProduceJsonProcessor.java │ │ │ │ │ ├── TestProduceProcessorManager.java │ │ │ │ │ └── TestProduceTextPlainProcessor.java │ │ │ │ └── query/ │ │ │ │ ├── QueryCodecCsvTest.java │ │ │ │ ├── QueryCodecMultiTest.java │ │ │ │ ├── QueryCodecPipesTest.java │ │ │ │ ├── QueryCodecSsvTest.java │ │ │ │ ├── QueryCodecTestBase.java │ │ │ │ └── QueryCodecsTest.java │ │ │ ├── definition/ │ │ │ │ ├── TestPath.java │ │ │ │ ├── TestRestOperationComparator.java │ │ │ │ ├── TestRestOperationMeta.java │ │ │ │ └── path/ │ │ │ │ ├── PathVarParamWriterTest.java │ │ │ │ ├── QueryVarParamWriterTest.java │ │ │ │ ├── URLPathBuilderTest.java │ │ │ │ └── URLPathStringBuilderTest.java │ │ │ ├── filter/ │ │ │ │ └── inner/ │ │ │ │ └── RestServerCodecFilterTest.java │ │ │ ├── locator/ │ │ │ │ ├── TestMicroservicePaths.java │ │ │ │ ├── TestPathSchema.java │ │ │ │ └── TestServicePathManager.java │ │ │ └── resource/ │ │ │ └── TestClassPathStaticResourceHandler.java │ │ └── resources/ │ │ ├── log4j2.xml │ │ ├── microservice.yaml │ │ └── web-root/ │ │ └── index.html │ └── pom.xml ├── core/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── core/ │ │ │ ├── BootListener.java │ │ │ ├── CoreConst.java │ │ │ ├── Endpoint.java │ │ │ ├── Invocation.java │ │ │ ├── NonSwaggerInvocation.java │ │ │ ├── ProducerProvider.java │ │ │ ├── SCBApplicationListener.java │ │ │ ├── SCBEngine.java │ │ │ ├── SCBStatus.java │ │ │ ├── ServiceCombCoreConfiguration.java │ │ │ ├── Transport.java │ │ │ ├── annotation/ │ │ │ │ └── Transport.java │ │ │ ├── bootstrap/ │ │ │ │ ├── SCBBootstrap.java │ │ │ │ └── SCBEngineForTest.java │ │ │ ├── bootup/ │ │ │ │ ├── BootUpInformationCollector.java │ │ │ │ ├── ConfigurationProblemsAlarmEvent.java │ │ │ │ ├── ConfigurationProblemsCollector.java │ │ │ │ ├── FilterChainCollector.java │ │ │ │ └── ServiceInformationCollector.java │ │ │ ├── definition/ │ │ │ │ ├── ConsumerMicroserviceVersionsMeta.java │ │ │ │ ├── CoreMetaUtils.java │ │ │ │ ├── InvocationRuntimeType.java │ │ │ │ ├── MicroserviceMeta.java │ │ │ │ ├── MicroserviceVersionsMeta.java │ │ │ │ ├── OperationConfig.java │ │ │ │ ├── OperationMeta.java │ │ │ │ └── SchemaMeta.java │ │ │ ├── event/ │ │ │ │ ├── InvocationBaseEvent.java │ │ │ │ ├── InvocationBusinessFinishEvent.java │ │ │ │ ├── InvocationBusinessMethodFinishEvent.java │ │ │ │ ├── InvocationBusinessMethodStartEvent.java │ │ │ │ ├── InvocationEncodeResponseStartEvent.java │ │ │ │ ├── InvocationFinishEvent.java │ │ │ │ ├── InvocationStartEvent.java │ │ │ │ ├── InvocationStartSendRequestEvent.java │ │ │ │ ├── InvocationTimeoutCheckEvent.java │ │ │ │ ├── InvocationWithResponseEvent.java │ │ │ │ └── ServerAccessLogEvent.java │ │ │ ├── exception/ │ │ │ │ ├── CoreExceptionConfiguration.java │ │ │ │ ├── CseException.java │ │ │ │ ├── DefaultExceptionProcessor.java │ │ │ │ ├── ExceptionCodes.java │ │ │ │ ├── ExceptionConverter.java │ │ │ │ ├── ExceptionProcessor.java │ │ │ │ ├── ExceptionUtils.java │ │ │ │ ├── Exceptions.java │ │ │ │ └── converter/ │ │ │ │ ├── ConnectTimeoutExceptionConverter.java │ │ │ │ ├── ConstraintViolationExceptionConverter.java │ │ │ │ ├── DefaultExceptionConverter.java │ │ │ │ ├── IllegalArgumentExceptionConverter.java │ │ │ │ ├── InvocationExceptionConverter.java │ │ │ │ ├── ServiceCombExceptionConverter.java │ │ │ │ ├── TimeoutExceptionConverter.java │ │ │ │ └── ValidateDetail.java │ │ │ ├── executor/ │ │ │ │ ├── ExecutorManager.java │ │ │ │ ├── GroupExecutor.java │ │ │ │ ├── GroupThreadFactory.java │ │ │ │ ├── LinkedBlockingQueueEx.java │ │ │ │ ├── ReactiveExecutor.java │ │ │ │ └── ThreadPoolExecutorEx.java │ │ │ ├── filter/ │ │ │ │ ├── AbstractFilter.java │ │ │ │ ├── ConsumerFilter.java │ │ │ │ ├── CoreFilterConfiguration.java │ │ │ │ ├── EdgeFilter.java │ │ │ │ ├── Filter.java │ │ │ │ ├── FilterChainsManager.java │ │ │ │ ├── FilterNode.java │ │ │ │ ├── InvocationFilterChains.java │ │ │ │ ├── ProviderFilter.java │ │ │ │ └── impl/ │ │ │ │ ├── ContextMapperFilter.java │ │ │ │ ├── EmptyFilter.java │ │ │ │ ├── JacksonPropertyNodeNameProvider.java │ │ │ │ ├── ParameterValidatorFilter.java │ │ │ │ ├── ProviderOperationFilter.java │ │ │ │ ├── RetryFilter.java │ │ │ │ └── ScheduleFilter.java │ │ │ ├── governance/ │ │ │ │ ├── CoreGovernanceConfiguration.java │ │ │ │ ├── GovernanceConfiguration.java │ │ │ │ ├── MatchType.java │ │ │ │ ├── RetryContext.java │ │ │ │ ├── ServiceCombCircuitBreakerExtension.java │ │ │ │ ├── ServiceCombConfigurationEventAdapter.java │ │ │ │ ├── ServiceCombInstanceIsolationExtension.java │ │ │ │ ├── ServiceCombMicroserviceMeta.java │ │ │ │ └── ServiceCombRetryExtension.java │ │ │ ├── invocation/ │ │ │ │ ├── CoreInvocationConfiguration.java │ │ │ │ ├── InvocationCreator.java │ │ │ │ ├── InvocationFactory.java │ │ │ │ ├── InvocationStageTrace.java │ │ │ │ ├── InvocationTimeoutBootListener.java │ │ │ │ ├── InvocationTimeoutStrategy.java │ │ │ │ ├── ProducerInvocationFlow.java │ │ │ │ ├── endpoint/ │ │ │ │ │ ├── EndpointCacheUtils.java │ │ │ │ │ ├── EndpointContextRegister.java │ │ │ │ │ ├── EndpointMapper.java │ │ │ │ │ ├── EndpointMapperFactory.java │ │ │ │ │ └── EndpointUtils.java │ │ │ │ └── timeout/ │ │ │ │ ├── PassingTimeStrategy.java │ │ │ │ └── ProcessingTimeStrategy.java │ │ │ ├── provider/ │ │ │ │ ├── LocalOpenAPIRegistry.java │ │ │ │ ├── OpenAPIRegistry.java │ │ │ │ ├── OpenAPIRegistryManager.java │ │ │ │ ├── RegistryOpenAPIRegistry.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── ConsumerProviderManager.java │ │ │ │ │ ├── InvokerUtils.java │ │ │ │ │ ├── MicroserviceReferenceConfig.java │ │ │ │ │ ├── ReactiveResponseExecutor.java │ │ │ │ │ ├── ReferenceConfig.java │ │ │ │ │ ├── ReferenceConfigManager.java │ │ │ │ │ └── SyncResponseExecutor.java │ │ │ │ └── producer/ │ │ │ │ ├── AbstractProducerProvider.java │ │ │ │ ├── ProducerBootListener.java │ │ │ │ ├── ProducerMeta.java │ │ │ │ └── ProducerProviderManager.java │ │ │ ├── registry/ │ │ │ │ └── discovery/ │ │ │ │ └── EndpointDiscoveryFilter.java │ │ │ ├── tracing/ │ │ │ │ ├── BraveTraceIdGenerator.java │ │ │ │ ├── ScbMarker.java │ │ │ │ ├── TraceIdGenerator.java │ │ │ │ └── TraceIdLogger.java │ │ │ └── transport/ │ │ │ ├── AbstractTransport.java │ │ │ ├── TransportClassAnnotationProcessor.java │ │ │ ├── TransportManager.java │ │ │ └── TransportMethodAnnotationProcessor.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── org.apache.servicecomb.core.exception.ExceptionConverter │ │ │ │ ├── org.apache.servicecomb.core.filter.FilterProvider │ │ │ │ ├── org.apache.servicecomb.core.tracing.TraceIdGenerator │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.SwaggerContextRegister │ │ │ │ └── org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── microservice.yaml │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── core/ │ │ ├── Config.java │ │ ├── TestConfig.java │ │ ├── TestConfigurationSpringInitializer.java │ │ ├── TestEndpoint.java │ │ ├── TestException.java │ │ ├── TestInvocation.java │ │ ├── TestSCBApplicationListener.java │ │ ├── TestTransport.java │ │ ├── consumer/ │ │ │ ├── TestReactiveResponseExecutor.java │ │ │ └── TestSyncResponseExecutor.java │ │ ├── definition/ │ │ │ ├── MicroServicePropertyExtendedStub.java │ │ │ ├── OperationConfigTest.java │ │ │ └── TestMicroserviceMetaManager.java │ │ ├── event/ │ │ │ └── TestInvocationStartEvent.java │ │ ├── exception/ │ │ │ ├── ExceptionsTest.java │ │ │ └── converter/ │ │ │ └── TimeoutExceptionConverterTest.java │ │ ├── executor/ │ │ │ ├── TestExecutorManager.java │ │ │ ├── TestGroupExecutor.java │ │ │ └── TestThreadPoolExecutorEx.java │ │ ├── filter/ │ │ │ ├── FilterChainTest.java │ │ │ └── impl/ │ │ │ ├── ParameterValidatorFilterTest.java │ │ │ └── ProducerOperationFilterTest.java │ │ ├── invocation/ │ │ │ ├── ProducerInvocationFlowTest.java │ │ │ ├── TestInvocationStageTrace.java │ │ │ ├── endpoint/ │ │ │ │ ├── EndpointTest.java │ │ │ │ └── EndpointUtilsTest.java │ │ │ └── timeout/ │ │ │ ├── PassingTimeStrategyTest.java │ │ │ └── ProcessingTimeStrategyTest.java │ │ ├── provider/ │ │ │ ├── Person.java │ │ │ ├── consumer/ │ │ │ │ └── TestInvokerUtils.java │ │ │ └── producer/ │ │ │ ├── TestProducerBootListener.java │ │ │ ├── TestProducerMeta.java │ │ │ └── TestProducerProviderManager.java │ │ ├── registry/ │ │ │ └── discovery/ │ │ │ └── TestEndpointDiscoveryFilter.java │ │ ├── tracing/ │ │ │ └── BraveTraceIdGeneratorTest.java │ │ └── transport/ │ │ ├── TestAbstractTransport.java │ │ └── TestTransportManager.java │ └── resources/ │ ├── META-INF/ │ │ └── spring/ │ │ └── cse.bean.xml │ ├── log4j2.xml │ ├── microservice.yaml │ └── test/ │ └── test/ │ ├── microservice.yaml │ └── schema.yaml ├── coverage-reports/ │ └── pom.xml ├── demo/ │ ├── README.md │ ├── assembly/ │ │ └── assembly.xml │ ├── demo-consul/ │ │ ├── README.md │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ClientWebsocketController.java │ │ │ │ ├── ConsulConsumerApplication.java │ │ │ │ ├── ConsumerController.java │ │ │ │ ├── ConsumerHeaderParamWithListSchema.java │ │ │ │ ├── ConsumerReactiveStreamController.java │ │ │ │ ├── ProviderService.java │ │ │ │ └── User.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ └── GatewayApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── pom.xml │ │ ├── provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ConsulProviderApplication.java │ │ │ │ ├── HeaderParamWithListSchema.java │ │ │ │ ├── ProviderController.java │ │ │ │ ├── ReactiveStreamController.java │ │ │ │ ├── User.java │ │ │ │ └── WebsocketController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test-client/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── Config.java │ │ │ │ ├── ConsulConfigIT.java │ │ │ │ ├── HeaderParamWithListSchemaIT.java │ │ │ │ ├── HelloWorldIT.java │ │ │ │ ├── ProviderIT.java │ │ │ │ ├── ReactiveStreamIT.java │ │ │ │ ├── TestClientApplication.java │ │ │ │ ├── ThirdSvcConfiguration.java │ │ │ │ ├── User.java │ │ │ │ └── WebsocketIT.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── samples/ │ │ └── ConsulIT.java │ ├── demo-crossapp/ │ │ ├── crossapp-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── crossapp/ │ │ │ │ │ ├── CrossappClient.java │ │ │ │ │ └── HelloWorld.java │ │ │ │ └── resources/ │ │ │ │ ├── log4j2.xml │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── crossapp/ │ │ │ └── CrossAppIT.java │ │ ├── crossapp-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── crossapp/ │ │ │ │ ├── CrossappServer.java │ │ │ │ └── HelloWorldImpl.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ └── pom.xml │ ├── demo-cse-v1/ │ │ ├── README.md │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── ConsumerConfigController.java │ │ │ │ ├── ConsumerConfigurationProperties.java │ │ │ │ ├── ConsumerController.java │ │ │ │ └── ProviderService.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ └── GatewayApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── pom.xml │ │ ├── provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── ProviderController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── provider-canary/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── ProviderController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── samples/ │ │ │ ├── Config.java │ │ │ ├── ConsumerConfigIT.java │ │ │ ├── HelloWorldIT.java │ │ │ └── TestClientApplication.java │ │ └── resources/ │ │ ├── application.yml │ │ └── log4j2.xml │ ├── demo-cse-v2/ │ │ ├── README.md │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── ConsumerConfigController.java │ │ │ │ ├── ConsumerConfigurationProperties.java │ │ │ │ ├── ConsumerController.java │ │ │ │ └── ProviderService.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ └── GatewayApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── pom.xml │ │ ├── provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── ProviderController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── samples/ │ │ │ ├── Config.java │ │ │ ├── ConsumerConfigIT.java │ │ │ ├── HelloWorldIT.java │ │ │ └── TestClientApplication.java │ │ └── resources/ │ │ ├── application.yml │ │ └── log4j2.xml │ ├── demo-edge/ │ │ ├── README.md │ │ ├── authentication/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── edge/ │ │ │ │ └── authentication/ │ │ │ │ ├── AuthImpl.java │ │ │ │ ├── AuthMain.java │ │ │ │ └── encrypt/ │ │ │ │ ├── EncryptImpl.java │ │ │ │ └── Hcr.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── business-1-1-0/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── edge/ │ │ │ │ └── business/ │ │ │ │ ├── BusinessMain_V1_1_0.java │ │ │ │ └── Impl.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── business-1.0.0/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── edge/ │ │ │ │ └── business/ │ │ │ │ ├── BusinessMain_V1_0_0.java │ │ │ │ └── Impl.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── business-2.0.0/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── edge/ │ │ │ │ └── business/ │ │ │ │ ├── BusinessMain_V2_0_0.java │ │ │ │ ├── EdgeServiceGovernanceService.java │ │ │ │ ├── Impl.java │ │ │ │ ├── ImplV2.java │ │ │ │ └── error/ │ │ │ │ ├── CustomExceptionToProducerResponseConverter.java │ │ │ │ ├── ErrorData.java │ │ │ │ ├── ErrorService.java │ │ │ │ └── IllegalStateErrorData.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── services/ │ │ │ │ └── org.apache.servicecomb.core.exception.ExceptionConverter │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── edge/ │ │ │ │ │ └── consumer/ │ │ │ │ │ ├── Consumer.java │ │ │ │ │ ├── ConsumerMain.java │ │ │ │ │ └── EdgeServiceGovernanceTest.java │ │ │ │ └── resources/ │ │ │ │ ├── log4j2.xml │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── edge/ │ │ │ └── consumer/ │ │ │ └── EdgeDemoIT.java │ │ ├── edge-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── edge/ │ │ │ │ ├── EdgeMain.java │ │ │ │ ├── authentication/ │ │ │ │ │ └── encrypt/ │ │ │ │ │ └── Hcr.java │ │ │ │ └── service/ │ │ │ │ ├── CustomResponseMetaMapper.java │ │ │ │ ├── EdgeConst.java │ │ │ │ ├── EdgeDispatcher.java │ │ │ │ ├── IllegalStateErrorData.java │ │ │ │ ├── encrypt/ │ │ │ │ │ ├── Encrypt.java │ │ │ │ │ ├── EncryptContext.java │ │ │ │ │ ├── EncryptEdgeDispatcher.java │ │ │ │ │ └── filter/ │ │ │ │ │ ├── DecodeBodyFilter.java │ │ │ │ │ ├── EdgeSignatureRequestFilter.java │ │ │ │ │ ├── EdgeSignatureResponseFilter.java │ │ │ │ │ └── UserIdFilter.java │ │ │ │ └── handler/ │ │ │ │ ├── Auth.java │ │ │ │ └── AuthHandler.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── services/ │ │ │ │ ├── org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper │ │ │ │ └── org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── model/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── edge/ │ │ │ └── model/ │ │ │ ├── AppClientDataRsp.java │ │ │ ├── ChannelRequestBase.java │ │ │ ├── DependTypeA.java │ │ │ ├── DependTypeB.java │ │ │ ├── RecursiveSelfType.java │ │ │ ├── ResultWithInstance.java │ │ │ └── User.java │ │ └── pom.xml │ ├── demo-etcd/ │ │ ├── README.md │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ClientWebsocketController.java │ │ │ │ ├── ConsumerController.java │ │ │ │ ├── ConsumerHeaderParamWithListSchema.java │ │ │ │ ├── ConsumerReactiveStreamController.java │ │ │ │ ├── EtcdConsumerApplication.java │ │ │ │ ├── ProviderService.java │ │ │ │ └── User.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ └── GatewayApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── pom.xml │ │ ├── provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── EtcdProviderApplication.java │ │ │ │ ├── HeaderParamWithListSchema.java │ │ │ │ ├── ProviderController.java │ │ │ │ ├── ReactiveStreamController.java │ │ │ │ ├── User.java │ │ │ │ └── WebsocketController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test-client/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── Config.java │ │ │ │ ├── EtcdConfigIT.java │ │ │ │ ├── HeaderParamWithListSchemaIT.java │ │ │ │ ├── HelloWorldIT.java │ │ │ │ ├── ProviderIT.java │ │ │ │ ├── ReactiveStreamIT.java │ │ │ │ ├── TestClientApplication.java │ │ │ │ ├── ThirdSvcConfiguration.java │ │ │ │ ├── User.java │ │ │ │ └── WebsocketIT.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── samples/ │ │ └── EtcdIT.java │ ├── demo-filter/ │ │ ├── filter-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── filter/ │ │ │ │ ├── FilterClient.java │ │ │ │ └── client/ │ │ │ │ ├── ClientExceptionSchema.java │ │ │ │ ├── GovernanceConsumerSchema.java │ │ │ │ └── RetryClientSchema.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── filter-edge/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── filter/ │ │ │ │ └── FilterEdge.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── filter-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── filter/ │ │ │ │ ├── FilterServer.java │ │ │ │ └── server/ │ │ │ │ ├── ExceptionSchema.java │ │ │ │ ├── GovernanceProviderSchema.java │ │ │ │ └── RetrySchema.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── filter-tests/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── filter/ │ │ │ │ │ ├── FilterTests.java │ │ │ │ │ └── tests/ │ │ │ │ │ ├── TestExceptionSchemaFromClient.java │ │ │ │ │ ├── TestGovernanceSchemaFromEdge.java │ │ │ │ │ ├── TestRetrySchemaFromClient.java │ │ │ │ │ └── TestRetrySchemaFromEdge.java │ │ │ │ └── resources/ │ │ │ │ ├── log4j2.xml │ │ │ │ ├── microservice.yaml │ │ │ │ ├── microservices/ │ │ │ │ │ └── com.servicecomb.filterEdge/ │ │ │ │ │ ├── GovernanceConsumerSchema.yaml │ │ │ │ │ └── RetryClientSchema.yaml │ │ │ │ └── registry.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── filter/ │ │ │ └── retry/ │ │ │ └── FilterTestsIT.java │ │ └── pom.xml │ ├── demo-jaxrs/ │ │ ├── jaxrs-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── jaxrs/ │ │ │ │ │ ├── JaxrsClient.java │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── CodeFirstRestTemplateJaxrs.java │ │ │ │ │ │ ├── CustomLoadbalanceExtensionsFactory.java │ │ │ │ │ │ ├── MultiErrorCodeServiceClient.java │ │ │ │ │ │ ├── SchemeInterfaceJaxrs.java │ │ │ │ │ │ ├── TestClientTimeout.java │ │ │ │ │ │ ├── TestCodeFirstJaxrs.java │ │ │ │ │ │ ├── TestCodeFirstJaxrsReactive.java │ │ │ │ │ │ ├── TestDynamicConfig.java │ │ │ │ │ │ ├── TestFileUploadSchema.java │ │ │ │ │ │ ├── TestFormRequestSchema.java │ │ │ │ │ │ ├── TestQueryParamSchema.java │ │ │ │ │ │ ├── TestQueryParamWithListSchema.java │ │ │ │ │ │ ├── TestReactiveSchema.java │ │ │ │ │ │ ├── TestSchemeInterfaceJaxrs.java │ │ │ │ │ │ ├── beanParam/ │ │ │ │ │ │ │ ├── BeanParamPojoClient.java │ │ │ │ │ │ │ ├── BeanParamRestTemplateClient.java │ │ │ │ │ │ │ └── BeanParamTestServiceIntf.java │ │ │ │ │ │ ├── injectBean/ │ │ │ │ │ │ │ └── TestInjectBeanSchema.java │ │ │ │ │ │ ├── pojoDefault/ │ │ │ │ │ │ │ ├── DefaultModel.java │ │ │ │ │ │ │ └── DefaultModelServiceClient.java │ │ │ │ │ │ └── validation/ │ │ │ │ │ │ └── ValidationServiceClient.java │ │ │ │ │ └── server/ │ │ │ │ │ └── pojoDefault/ │ │ │ │ │ └── DefaultResponseModel.java │ │ │ │ └── resources/ │ │ │ │ ├── log4j2.xml │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── jaxrs/ │ │ │ └── JaxrsIT.java │ │ ├── jaxrs-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── jaxrs/ │ │ │ │ ├── JaxrsServer.java │ │ │ │ └── server/ │ │ │ │ ├── CodeFirstJaxrs.java │ │ │ │ ├── ComputeImpl.java │ │ │ │ ├── FileUploadSchema.java │ │ │ │ ├── FormRequestSchema.java │ │ │ │ ├── JaxRSDefaultValues.java │ │ │ │ ├── QueryParamSchema.java │ │ │ │ ├── QueryParamWithListSchema.java │ │ │ │ ├── ReactiveSchema.java │ │ │ │ ├── RequestClientTimeOut.java │ │ │ │ ├── SchemeInterfaceJaxrs.java │ │ │ │ ├── SchemeInterfaceJaxrsImpl.java │ │ │ │ ├── Validator.java │ │ │ │ ├── beanParam/ │ │ │ │ │ ├── BeanParamTestService.java │ │ │ │ │ ├── TestBeanParameter.java │ │ │ │ │ └── TestBeanParameterWithUpload.java │ │ │ │ ├── injectBean/ │ │ │ │ │ ├── InjectBean.java │ │ │ │ │ ├── InjectBeanSchema.java │ │ │ │ │ └── InjectBeanVertxHttpDispatcher.java │ │ │ │ ├── multiErrorCode/ │ │ │ │ │ ├── MultiErrorCodeService.java │ │ │ │ │ ├── NoClientErrorCode200.java │ │ │ │ │ └── NoClientErrorCode400.java │ │ │ │ ├── pojoDefault/ │ │ │ │ │ ├── DefaultModelService.java │ │ │ │ │ ├── DefaultRequestModel.java │ │ │ │ │ └── DefaultResponseModel.java │ │ │ │ └── validation/ │ │ │ │ └── ValidationService.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── services/ │ │ │ │ └── org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ └── pom.xml │ ├── demo-local-registry/ │ │ ├── README.md │ │ ├── demo-local-registry-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── localRegistryClient/ │ │ │ │ │ ├── CodeFirstService.java │ │ │ │ │ ├── LocalRegistryClientApplication.java │ │ │ │ │ ├── LocalRegistryServerTest.java │ │ │ │ │ ├── RegistryBeansConfiguration.java │ │ │ │ │ └── ServerService.java │ │ │ │ └── resources/ │ │ │ │ ├── application.yml │ │ │ │ ├── log4j2.xml │ │ │ │ ├── microservices/ │ │ │ │ │ ├── demo-local-registry-server/ │ │ │ │ │ │ └── ServerEndpoint.yaml │ │ │ │ │ ├── demo-local-registry-server-bean/ │ │ │ │ │ │ └── ServerEndpoint.yaml │ │ │ │ │ └── demo-local-registry-server-bean2/ │ │ │ │ │ └── ServerEndpoint.yaml │ │ │ │ └── registry.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── localRegistryClient/ │ │ │ └── LocalRegistryIT.java │ │ ├── demo-local-registry-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── localRegistryServer/ │ │ │ │ ├── CodeFirstEndpoint.java │ │ │ │ ├── LocalRegistryServerApplication.java │ │ │ │ ├── SelfServiceInvoker.java │ │ │ │ └── ServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── pom.xml │ ├── demo-multi-registries/ │ │ ├── README.md │ │ ├── demo-multi-registries-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── registry/ │ │ │ │ │ ├── IServerEndpoint.java │ │ │ │ │ ├── IServiceCenterEndpoint.java │ │ │ │ │ ├── MultiRegistriesClientApplication.java │ │ │ │ │ ├── MultiRegistriesServerTestCase.java │ │ │ │ │ ├── SchemaDiscoveryTestCase.java │ │ │ │ │ ├── ServiceCenterEndpoint.java │ │ │ │ │ └── ServiceCenterTestCase.java │ │ │ │ └── resources/ │ │ │ │ ├── application.yml │ │ │ │ ├── log4j2.xml │ │ │ │ ├── microservices/ │ │ │ │ │ └── thirdParty-service-center/ │ │ │ │ │ └── ServiceCenterEndpoint.yaml │ │ │ │ └── registry.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── registry/ │ │ │ └── MultiRegistriesIT.java │ │ ├── demo-multi-registries-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── registry/ │ │ │ │ ├── MultiRegistriesServerApplication.java │ │ │ │ ├── SelfServiceInvoker.java │ │ │ │ └── ServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── pom.xml │ ├── demo-multi-service-center/ │ │ ├── demo-multi-service-center-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── multiServiceCenterClient/ │ │ │ │ │ ├── Application.java │ │ │ │ │ ├── IConfigurationEndpoint.java │ │ │ │ │ ├── IServerEndpoint.java │ │ │ │ │ ├── RegistryClientTest.java │ │ │ │ │ ├── SC2Configuration.java │ │ │ │ │ ├── SC2Discovery.java │ │ │ │ │ ├── SC2Registration.java │ │ │ │ │ ├── ServerATest.java │ │ │ │ │ └── ServerBTest.java │ │ │ │ └── resources/ │ │ │ │ ├── application.yml │ │ │ │ └── log4j2.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── multiServiceCenterClient/ │ │ │ └── MultiServiceCenterIT.java │ │ ├── demo-multi-service-center-serverA/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiServiceCenter/ │ │ │ │ ├── ServerApplication.java │ │ │ │ └── ServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── demo-multi-service-center-serverB/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiServiceCenterServerB/ │ │ │ │ ├── ConfigurationEndpoint.java │ │ │ │ ├── ServerApplication.java │ │ │ │ └── ServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.properties │ │ │ ├── application.yml │ │ │ ├── log4j2.xml │ │ │ ├── mapping.yaml │ │ │ └── microservice.yaml │ │ └── pom.xml │ ├── demo-multiple/ │ │ ├── a-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiple/ │ │ │ │ └── a/ │ │ │ │ └── client/ │ │ │ │ ├── AClient.java │ │ │ │ ├── AClientMain.java │ │ │ │ └── AIntf.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── a-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiple/ │ │ │ │ └── a/ │ │ │ │ └── server/ │ │ │ │ ├── AImpl.java │ │ │ │ └── AServerMain.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── b-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiple/ │ │ │ │ └── b/ │ │ │ │ └── client/ │ │ │ │ ├── BClient.java │ │ │ │ ├── BClientMain.java │ │ │ │ └── BIntf.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── b-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiple/ │ │ │ │ └── b/ │ │ │ │ └── server/ │ │ │ │ ├── BImpl.java │ │ │ │ └── BServerMain.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ ├── multiple-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── multiple/ │ │ │ │ │ └── client/ │ │ │ │ │ └── MultipleClient.java │ │ │ │ └── resources/ │ │ │ │ ├── log4j2.xml │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── multiple/ │ │ │ └── client/ │ │ │ └── MultipleIT.java │ │ ├── multiple-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── multiple/ │ │ │ │ └── server/ │ │ │ │ └── MultipleServer.java │ │ │ └── resources/ │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ └── pom.xml │ ├── demo-nacos/ │ │ ├── README.md │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── ConsumerController.java │ │ │ │ ├── ConsumerHeaderParamWithListSchema.java │ │ │ │ └── ProviderService.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ └── GatewayApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── pom.xml │ │ ├── provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── HeaderParamWithListSchema.java │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── ProviderController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test-client/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── Config.java │ │ │ │ ├── HeaderParamWithListSchemaIT.java │ │ │ │ ├── HelloWorldIT.java │ │ │ │ └── TestClientApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── samples/ │ │ └── NocasIT.java │ ├── demo-pojo/ │ │ ├── pojo-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── pojo/ │ │ │ │ │ └── client/ │ │ │ │ │ ├── BeanRpcTest.java │ │ │ │ │ ├── ClientInterfaceForServerTest.java │ │ │ │ │ ├── CodeFirstPojoClient.java │ │ │ │ │ ├── CodeFirstPojoClientIntf.java │ │ │ │ │ ├── PojoClient.java │ │ │ │ │ ├── SchemaInterface.java │ │ │ │ │ ├── SchemeInterfacePojo.java │ │ │ │ │ ├── TestFlowControl.java │ │ │ │ │ ├── TestNotRecommendedService.java │ │ │ │ │ ├── TestRpcReferenceMethodInjection.java │ │ │ │ │ ├── TestSameService.java │ │ │ │ │ ├── TestSchemeInterface.java │ │ │ │ │ ├── TestSchemeInterfacePojo.java │ │ │ │ │ ├── TestTestImpl.java │ │ │ │ │ ├── TestWeakPojo.java │ │ │ │ │ └── invoker/ │ │ │ │ │ ├── ClientModel.java │ │ │ │ │ └── TestInvokerEndpoint.java │ │ │ │ └── resources/ │ │ │ │ ├── META-INF/ │ │ │ │ │ └── spring/ │ │ │ │ │ └── pojo.client.bean.xml │ │ │ │ ├── log4j2.xml │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── pojo/ │ │ │ └── PojoIT.java │ │ ├── pojo-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── pojo/ │ │ │ │ └── server/ │ │ │ │ ├── CodeFirstPojo.java │ │ │ │ ├── FlowControlClientSchema.java │ │ │ │ ├── FlowControlSchema.java │ │ │ │ ├── HelloImpl.java │ │ │ │ ├── NotRecommendedService.java │ │ │ │ ├── PojoProducersCustomized.java │ │ │ │ ├── PojoServer.java │ │ │ │ ├── SchemaInterface.java │ │ │ │ ├── SchemaInterfaceImpl.java │ │ │ │ ├── SchemeInterfacePojo.java │ │ │ │ ├── SchemeInterfacePojoImpl.java │ │ │ │ ├── SmartCareImpl.java │ │ │ │ ├── TestImpl.java │ │ │ │ ├── WeakPojo.java │ │ │ │ ├── invoker/ │ │ │ │ │ ├── InvokerEndpoint.java │ │ │ │ │ └── ServerModel.java │ │ │ │ └── same/ │ │ │ │ ├── pk1/ │ │ │ │ │ ├── SameInterface.java │ │ │ │ │ ├── SameInterfaceImpl.java │ │ │ │ │ └── SameService.java │ │ │ │ └── pk2/ │ │ │ │ ├── SameInterface.java │ │ │ │ ├── SameInterfaceImpl.java │ │ │ │ └── SameService.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── spring/ │ │ │ │ └── pojo.server.bean.xml │ │ │ ├── log4j2.xml │ │ │ └── microservice.yaml │ │ └── pom.xml │ ├── demo-register-url-prefix/ │ │ ├── README.md │ │ ├── demo-register-url-prefix-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── prefix/ │ │ │ │ │ └── Application.java │ │ │ │ └── resources/ │ │ │ │ ├── application.yml │ │ │ │ └── log4j2.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── prefix/ │ │ │ └── RegisterUrlPrefixIT.java │ │ ├── demo-register-url-prefix-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── prefix/ │ │ │ │ ├── PrefixApplication.java │ │ │ │ └── RegisterUrlPrefixEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ ├── log4j2.xml │ │ │ └── logback.xml │ │ └── pom.xml │ ├── demo-schema/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ ├── CategorizedTestCase.java │ │ │ ├── CategorizedTestCaseRunner.java │ │ │ ├── CodeFirstPojoIntf.java │ │ │ ├── CodeFirstRestTemplate.java │ │ │ ├── CommonSchemaInterface.java │ │ │ ├── DemoConst.java │ │ │ ├── DemoSSLCustom.java │ │ │ ├── EmptyObject.java │ │ │ ├── Generic.java │ │ │ ├── RestObjectMapperWithStringMapper.java │ │ │ ├── RestObjectMapperWithStringMapperNotWriteNull.java │ │ │ ├── TestMgr.java │ │ │ ├── api/ │ │ │ │ ├── IHeaderParamWithListSchema.java │ │ │ │ └── IHeaderParamWithListSchemaSpringMvc.java │ │ │ ├── compute/ │ │ │ │ ├── Compute.java │ │ │ │ ├── GenericParam.java │ │ │ │ ├── GenericParamExtended.java │ │ │ │ ├── GenericParamWithJsonIgnore.java │ │ │ │ └── Person.java │ │ │ ├── controller/ │ │ │ │ ├── Controller.java │ │ │ │ ├── Person.java │ │ │ │ └── PersonAlias.java │ │ │ ├── helloworld/ │ │ │ │ └── greeter/ │ │ │ │ └── Hello.java │ │ │ ├── ignore/ │ │ │ │ ├── IgnoreInterface.java │ │ │ │ ├── InputModelForTestIgnore.java │ │ │ │ └── OutputModelForTestIgnore.java │ │ │ ├── jaxbbean/ │ │ │ │ ├── JAXBJob.java │ │ │ │ └── JAXBPerson.java │ │ │ ├── jaxrs/ │ │ │ │ └── server/ │ │ │ │ └── validation/ │ │ │ │ └── ValidationModel.java │ │ │ ├── mapnull/ │ │ │ │ ├── ParseRequest.java │ │ │ │ └── ParseResponse.java │ │ │ ├── model/ │ │ │ │ └── SpecialNameModel.java │ │ │ ├── multiErrorCode/ │ │ │ │ ├── MultiRequest.java │ │ │ │ ├── MultiResponse200.java │ │ │ │ ├── MultiResponse400.java │ │ │ │ └── MultiResponse500.java │ │ │ ├── produceprocessor/ │ │ │ │ ├── ProduceAppXmlProcessor.java │ │ │ │ └── override/ │ │ │ │ └── ProduceAppXmlProcessor.java │ │ │ ├── server/ │ │ │ │ ├── AbstractModel.java │ │ │ │ ├── DefaultAbstractModel.java │ │ │ │ ├── GenericsModel.java │ │ │ │ ├── MapModel.java │ │ │ │ ├── NotRecommendedServiceInf.java │ │ │ │ ├── SecondAbstractModel.java │ │ │ │ ├── Test.java │ │ │ │ ├── TestRequest.java │ │ │ │ ├── TestResponse.java │ │ │ │ ├── User.java │ │ │ │ └── WrappedAbstractModel.java │ │ │ ├── smartcare/ │ │ │ │ ├── Application.java │ │ │ │ ├── Group.java │ │ │ │ ├── Response.java │ │ │ │ └── SmartCare.java │ │ │ ├── utils/ │ │ │ │ └── JAXBUtils.java │ │ │ └── validator/ │ │ │ ├── Student.java │ │ │ └── Teacher.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ └── org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor │ │ └── microservice.yaml │ ├── demo-spring-boot-transport/ │ │ ├── demo-spring-boot-pojo-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── springboot/ │ │ │ │ │ └── pojo/ │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── DemoConst.java │ │ │ │ │ │ ├── PojoClient.java │ │ │ │ │ │ ├── PojoClientTest.java │ │ │ │ │ │ └── TestMgr.java │ │ │ │ │ └── server/ │ │ │ │ │ └── schema/ │ │ │ │ │ └── server/ │ │ │ │ │ ├── Test.java │ │ │ │ │ ├── TestRequest.java │ │ │ │ │ ├── TestResponse.java │ │ │ │ │ └── User.java │ │ │ │ └── resources/ │ │ │ │ ├── META-INF/ │ │ │ │ │ └── spring/ │ │ │ │ │ └── pojo.client.bean.xml │ │ │ │ └── application.yml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── springboot/ │ │ │ └── pojo/ │ │ │ └── client/ │ │ │ └── PojoClientIT.java │ │ ├── demo-spring-boot-pojo-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── springboot/ │ │ │ │ └── pojo/ │ │ │ │ └── server/ │ │ │ │ ├── PojoServer.java │ │ │ │ ├── handler/ │ │ │ │ │ └── MyHandler.java │ │ │ │ └── schema/ │ │ │ │ ├── greeter/ │ │ │ │ │ └── Hello.java │ │ │ │ └── server/ │ │ │ │ ├── HelloImpl.java │ │ │ │ ├── Test.java │ │ │ │ ├── TestImpl.java │ │ │ │ ├── TestRequest.java │ │ │ │ ├── TestResponse.java │ │ │ │ └── User.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── demo-spring-boot-springmvc-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── springboot/ │ │ │ │ │ └── springmvc/ │ │ │ │ │ └── client/ │ │ │ │ │ ├── PlaceHolderSchemaTest.java │ │ │ │ │ ├── ReactiveStreamIT.java │ │ │ │ │ ├── SpringMvcClient.java │ │ │ │ │ ├── TestAnnotationsSchema.java │ │ │ │ │ ├── ThirdSvcConfiguration.java │ │ │ │ │ └── UploadDownloadSchemaTest.java │ │ │ │ └── resources/ │ │ │ │ ├── application.yml │ │ │ │ ├── certificates/ │ │ │ │ │ ├── server.p12 │ │ │ │ │ └── trust.jks │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── springboot/ │ │ │ └── springmvc/ │ │ │ └── client/ │ │ │ └── SpringMvcClientIT.java │ │ ├── demo-spring-boot-springmvc-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── springboot/ │ │ │ │ └── springmvc/ │ │ │ │ └── server/ │ │ │ │ ├── AnnotationsSchema.java │ │ │ │ ├── PlaceHolderSchema.java │ │ │ │ ├── ReactiveStreamController.java │ │ │ │ ├── SpringmvcServer.java │ │ │ │ └── UploadDownloadSchema.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ ├── certificates/ │ │ │ │ ├── server.p12 │ │ │ │ └── trust.jks │ │ │ └── microservice.yaml │ │ └── pom.xml │ ├── demo-springmvc/ │ │ ├── pom.xml │ │ ├── springmvc-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── springmvc/ │ │ │ │ │ ├── SpringmvcClient.java │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── CodeFirstRestTemplateSpringmvc.java │ │ │ │ │ │ ├── CodeFirstSpringmvcIntf.java │ │ │ │ │ │ ├── CustomEndpointDiscoveryFilter.java │ │ │ │ │ │ ├── ICompatible1xTestSchema.java │ │ │ │ │ │ ├── ResponseOKData.java │ │ │ │ │ │ ├── SchemeInterfaceSpringmvc.java │ │ │ │ │ │ ├── SpringMVCSchema.java │ │ │ │ │ │ ├── TestAnnotationsSchema.java │ │ │ │ │ │ ├── TestBigNumberSchema.java │ │ │ │ │ │ ├── TestContentType.java │ │ │ │ │ │ ├── TestControllerImpl.java │ │ │ │ │ │ ├── TestDataTypesAnnotationsSchema.java │ │ │ │ │ │ ├── TestDateTimeSchema.java │ │ │ │ │ │ ├── TestDownloadSchema.java │ │ │ │ │ │ ├── TestFactoryBean.java │ │ │ │ │ │ ├── TestGeneric.java │ │ │ │ │ │ ├── TestInvokeWhenServerNotReady.java │ │ │ │ │ │ ├── TestManagementEndpoint.java │ │ │ │ │ │ ├── TestMaxHttpUrlLength.java │ │ │ │ │ │ ├── TestObject.java │ │ │ │ │ │ ├── TestResponse.java │ │ │ │ │ │ ├── TestRestTemplate.java │ │ │ │ │ │ ├── TestRetrySchema.java │ │ │ │ │ │ ├── TestSchemeInterfaceSpringmvc.java │ │ │ │ │ │ ├── TestSpringMVCCommonSchemaInterface.java │ │ │ │ │ │ ├── TestThirdPartyRegistration.java │ │ │ │ │ │ ├── TestThirdSvc.java │ │ │ │ │ │ ├── TestTransportSchema.java │ │ │ │ │ │ ├── TestUploadSchema.java │ │ │ │ │ │ ├── TestWeakSpringmvc.java │ │ │ │ │ │ ├── ThirdPartyService.java │ │ │ │ │ │ ├── ThirdSvc.java │ │ │ │ │ │ └── factory/ │ │ │ │ │ │ ├── ServiceBean.java │ │ │ │ │ │ ├── ServiceFactoryBean.java │ │ │ │ │ │ ├── ServiceWithReference.java │ │ │ │ │ │ └── ServiceWithReferenceImpl.java │ │ │ │ │ └── decoderesponse/ │ │ │ │ │ └── DecodeTestResponse.java │ │ │ │ └── resources/ │ │ │ │ ├── META-INF/ │ │ │ │ │ └── spring/ │ │ │ │ │ └── springmvc.client.bean.xml │ │ │ │ ├── SpringMVCSchema.yaml │ │ │ │ ├── certificates/ │ │ │ │ │ ├── server.p12 │ │ │ │ │ └── trust.jks │ │ │ │ ├── log4j2.xml │ │ │ │ └── microservice.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── springmvc/ │ │ │ └── SpringMvcIT.java │ │ └── springmvc-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── springmvc/ │ │ │ ├── SpringmvcServer.java │ │ │ ├── decoderesponse/ │ │ │ │ └── DecodeTestResponse.java │ │ │ ├── filter/ │ │ │ │ └── ProviderTestFilter.java │ │ │ ├── server/ │ │ │ │ ├── AnnotationsSchema.java │ │ │ │ ├── BigNumberSchema.java │ │ │ │ ├── BizkeeperTest.java │ │ │ │ ├── CodeFirstSpringmvc.java │ │ │ │ ├── CodeFirstSpringmvcForSchema.java │ │ │ │ ├── Compatible1xTestSchema.java │ │ │ │ ├── CompatibleQueryBean.java │ │ │ │ ├── ConfigurationProblemsCollectorTest.java │ │ │ │ ├── ContentTypeSpringmvc.java │ │ │ │ ├── ContentTypeSpringmvcOverwrite.java │ │ │ │ ├── ControllerImpl.java │ │ │ │ ├── DataTypesAnnotationsSchema.java │ │ │ │ ├── DateTimeSchema.java │ │ │ │ ├── DownloadSchema.java │ │ │ │ ├── MyStrategy.java │ │ │ │ ├── MyStrategyFactory.java │ │ │ │ ├── ProducerTestsAfterBootup.java │ │ │ │ ├── ReadFileSource.java │ │ │ │ ├── ResponseOKData.java │ │ │ │ ├── ResponseOKException.java │ │ │ │ ├── ResponseOKExceptionConverter.java │ │ │ │ ├── RestProducersCustomized.java │ │ │ │ ├── RetrySchema.java │ │ │ │ ├── SchemeInterfaceSpringmvc.java │ │ │ │ ├── SchemeInterfaceSpringmvcImpl.java │ │ │ │ ├── SpringMVCCommonSchemaInterface.java │ │ │ │ ├── SpringMvcDefaultValues.java │ │ │ │ ├── TransportSchema.java │ │ │ │ ├── UploadSchema.java │ │ │ │ └── WeakSpringmvc.java │ │ │ └── third/ │ │ │ ├── EarlyConsumer.java │ │ │ ├── HealthSchema.java │ │ │ ├── HeartBeatService.java │ │ │ ├── NormalConsumer.java │ │ │ └── Register.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ ├── org.apache.servicecomb.core.exception.ExceptionConverter │ │ │ └── org.apache.servicecomb.qps.strategy.IStrategyFactory │ │ ├── certificates/ │ │ │ ├── server.p12 │ │ │ └── trust.jks │ │ ├── log4j2.xml │ │ ├── microservice.yaml │ │ └── schemas/ │ │ └── CodeFirstSpringmvcForSchema.yaml │ ├── demo-zeroconfig-registry/ │ │ ├── README.md │ │ ├── demo-zeroconfig-registry-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── zeroconfig/ │ │ │ │ └── client/ │ │ │ │ ├── ClientApplication.java │ │ │ │ ├── ClientModel.java │ │ │ │ ├── ClientServerEndpoint.java │ │ │ │ ├── GovernanceEndpoint.java │ │ │ │ ├── IRpcEndpoint.java │ │ │ │ └── IServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── demo-zeroconfig-registry-edge/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── zeroconfig/ │ │ │ │ └── edge/ │ │ │ │ ├── EdgeApplication.java │ │ │ │ ├── SelfServiceInvoker.java │ │ │ │ └── ServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── demo-zeroconfig-registry-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── demo/ │ │ │ │ └── zeroconfig/ │ │ │ │ └── server/ │ │ │ │ ├── GovernanceEndpoint.java │ │ │ │ ├── GovernanceNoPrefixEndpoint.java │ │ │ │ ├── RpcEndpoint.java │ │ │ │ ├── SelfServiceInvoker.java │ │ │ │ ├── ServerApplication.java │ │ │ │ └── ServerEndpoint.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── demo-zeroconfig-registry-tests/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── demo/ │ │ │ │ │ └── zeroconfig/ │ │ │ │ │ └── tests/ │ │ │ │ │ ├── Application.java │ │ │ │ │ ├── ClientModel.java │ │ │ │ │ ├── GovernanceTest.java │ │ │ │ │ └── ServerTest.java │ │ │ │ └── resources/ │ │ │ │ ├── application.yml │ │ │ │ ├── logback.xml │ │ │ │ ├── microservices/ │ │ │ │ │ ├── demo-zeroconfig-registry-client/ │ │ │ │ │ │ ├── ClientServerEndpoint.yaml │ │ │ │ │ │ ├── GovernanceEndpoint.yaml │ │ │ │ │ │ └── SchemaDiscoveryEndpoint.yaml │ │ │ │ │ └── demo-zeroconfig-registry-edge/ │ │ │ │ │ ├── ClientServerEndpoint.yaml │ │ │ │ │ └── SchemaDiscoveryEndpoint.yaml │ │ │ │ └── registry.yaml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── demo/ │ │ │ └── zeroconfig/ │ │ │ └── tests/ │ │ │ └── ZeroConfigRegistryIT.java │ │ └── pom.xml │ ├── demo-zookeeper/ │ │ ├── README.md │ │ ├── consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── ClientWebsocketController.java │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── ConsumerController.java │ │ │ │ ├── ConsumerHeaderParamWithListSchema.java │ │ │ │ ├── ConsumerReactiveStreamController.java │ │ │ │ └── ProviderService.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── gateway/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ └── GatewayApplication.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ ├── pom.xml │ │ ├── provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── HeaderParamWithListSchema.java │ │ │ │ ├── ProviderApplication.java │ │ │ │ ├── ProviderController.java │ │ │ │ ├── ReactiveStreamController.java │ │ │ │ └── WebsocketController.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test-client/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── samples/ │ │ │ │ ├── Config.java │ │ │ │ ├── HeaderParamWithListSchemaIT.java │ │ │ │ ├── HelloWorldIT.java │ │ │ │ ├── ReactiveStreamIT.java │ │ │ │ ├── TestClientApplication.java │ │ │ │ ├── ThirdSvcConfiguration.java │ │ │ │ └── WebsocketIT.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── log4j2.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── samples/ │ │ └── ZookeeperIT.java │ └── pom.xml ├── dependencies/ │ ├── bom/ │ │ └── pom.xml │ ├── default/ │ │ └── pom.xml │ └── pom.xml ├── distribution/ │ ├── pom.xml │ └── src/ │ ├── assembly/ │ │ ├── bin.xml │ │ └── src.xml │ └── release/ │ ├── LICENSE │ ├── NOTICE │ └── licenses/ │ ├── LICENSE-abego │ ├── LICENSE-animalsniffer │ ├── LICENSE-antlr │ ├── LICENSE-asm │ ├── LICENSE-bouncycastle │ ├── LICENSE-cc0 │ ├── LICENSE-cddl │ ├── LICENSE-checkerqual │ ├── LICENSE-edl-v10 │ ├── LICENSE-epl-v10 │ ├── LICENSE-epl-v20 │ ├── LICENSE-hamcrest │ ├── LICENSE-hdrhistogram │ ├── LICENSE-icu4j │ ├── LICENSE-jcodings │ ├── LICENSE-joni │ ├── LICENSE-jopt │ ├── LICENSE-jsonp │ ├── LICENSE-jsoup │ ├── LICENSE-jsr311-api │ ├── LICENSE-logback │ ├── LICENSE-mozilla-v20 │ ├── LICENSE-protobuf │ ├── LICENSE-slf4j │ ├── LICENSE-woodstox-stax2-api │ ├── NOTICE-apache-commons-codec │ ├── NOTICE-netty │ ├── NOTICE-prometheus │ ├── NOTICE-protostuff │ └── NOTICE-tomcat ├── dynamic-config/ │ ├── config-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── config/ │ │ │ │ └── apollo/ │ │ │ │ ├── ApolloClient.java │ │ │ │ ├── ApolloConfig.java │ │ │ │ ├── ApolloConfiguration.java │ │ │ │ ├── ApolloDynamicPropertiesSource.java │ │ │ │ └── ConfigurationAction.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.config.DynamicPropertiesSource │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ └── apollo/ │ │ │ └── ApolloClientTest.java │ │ └── resources/ │ │ └── microservice.yaml │ ├── config-cc/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── config/ │ │ │ │ └── cc/ │ │ │ │ ├── ConfigCenterConfig.java │ │ │ │ ├── ConfigCenterConfiguration.java │ │ │ │ ├── ConfigCenterDynamicPropertiesSource.java │ │ │ │ ├── ConfigCenterInformationCollector.java │ │ │ │ └── TransportUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── org.apache.servicecomb.config.DynamicPropertiesSource │ │ │ │ └── org.apache.servicecomb.core.bootup.BootUpInformationCollector │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ └── cc/ │ │ │ ├── ConfigCenterAddressManagerTest.java │ │ │ └── ConfigCenterConfigurationSourceImplTest.java │ │ └── resources/ │ │ └── microservice.yaml │ ├── config-consul/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ └── consul/ │ │ │ ├── ConsulConfig.java │ │ │ ├── ConsulConfigClient.java │ │ │ ├── ConsulConfigProperties.java │ │ │ └── ConsulDynamicPropertiesSource.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.config.DynamicPropertiesSource │ ├── config-etcd/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ └── etcd/ │ │ │ ├── EtcdClient.java │ │ │ ├── EtcdConfig.java │ │ │ └── EtcdDynamicPropertiesSource.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.config.DynamicPropertiesSource │ ├── config-kie/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── config/ │ │ │ │ └── kie/ │ │ │ │ ├── KieConfig.java │ │ │ │ ├── KieConfigConfiguration.java │ │ │ │ ├── KieDynamicPropertiesSource.java │ │ │ │ ├── TransportUtils.java │ │ │ │ └── collect/ │ │ │ │ └── KieClientInformationCollector.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── org.apache.servicecomb.config.DynamicPropertiesSource │ │ │ │ └── org.apache.servicecomb.core.bootup.BootUpInformationCollector │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── resources/ │ │ └── microservice.yaml │ ├── config-nacos/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── config/ │ │ │ │ └── nacos/ │ │ │ │ ├── NacosClient.java │ │ │ │ ├── NacosConfig.java │ │ │ │ └── NacosDynamicPropertiesSource.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── org.apache.servicecomb.config.DynamicPropertiesSource │ │ └── test/ │ │ └── resources/ │ │ └── microservice.yaml │ ├── config-zookeeper/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ └── zookeeper/ │ │ │ ├── ZookeeperClient.java │ │ │ ├── ZookeeperConfig.java │ │ │ └── ZookeeperDynamicPropertiesSource.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.config.DynamicPropertiesSource │ └── pom.xml ├── edge/ │ ├── edge-core/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── edge/ │ │ │ │ └── core/ │ │ │ │ ├── AbstractEdgeDispatcher.java │ │ │ │ ├── CommonHttpEdgeDispatcher.java │ │ │ │ ├── DefaultEdgeDispatcher.java │ │ │ │ ├── EdgeAddHeaderFilter.java │ │ │ │ ├── EdgeBootListener.java │ │ │ │ ├── EdgeCoreConfiguration.java │ │ │ │ ├── EdgeInvocationCreator.java │ │ │ │ ├── EdgeRestServerVerticle.java │ │ │ │ └── URLMappedEdgeDispatcher.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── microservice.yaml │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── edge/ │ │ └── core/ │ │ ├── TestAbstractEdgeDispatcher.java │ │ ├── TestURLMappedEdgeDispatcher.java │ │ └── TestUtils.java │ └── pom.xml ├── etc/ │ ├── code-templates.xml │ ├── eclipse-java-google-style.xml │ └── intellij-java-google-style.xml ├── foundations/ │ ├── foundation-common/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── foundation/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── Cipher.java │ │ │ │ │ ├── DefaultCipher.java │ │ │ │ │ ├── FoundationCommonAuthConfiguration.java │ │ │ │ │ └── ShaAKSKCipher.java │ │ │ │ ├── common/ │ │ │ │ │ ├── AbstractObjectManager.java │ │ │ │ │ ├── CommonThread.java │ │ │ │ │ ├── DynamicObject.java │ │ │ │ │ ├── Holder.java │ │ │ │ │ ├── LegacyPropertyFactory.java │ │ │ │ │ ├── NamedThreadFactory.java │ │ │ │ │ ├── ParameterizedTypeUtil.java │ │ │ │ │ ├── RegisterManager.java │ │ │ │ │ ├── VendorExtensions.java │ │ │ │ │ ├── Version.java │ │ │ │ │ ├── base/ │ │ │ │ │ │ ├── DescriptiveRunnable.java │ │ │ │ │ │ ├── DynamicEnum.java │ │ │ │ │ │ ├── DynamicEnumCache.java │ │ │ │ │ │ ├── EnumUtils.java │ │ │ │ │ │ └── ServiceCombConstants.java │ │ │ │ │ ├── cache/ │ │ │ │ │ │ └── VersionedCache.java │ │ │ │ │ ├── concurrency/ │ │ │ │ │ │ └── SuppressedRunnableWrapper.java │ │ │ │ │ ├── concurrent/ │ │ │ │ │ │ └── ConcurrentHashMapEx.java │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── ConfigLoader.java │ │ │ │ │ │ ├── PaaSResourceUtils.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── AbstractLoader.java │ │ │ │ │ │ ├── PaaSPropertiesLoaderUtils.java │ │ │ │ │ │ ├── PropertiesLoader.java │ │ │ │ │ │ └── XmlLoaderUtils.java │ │ │ │ │ ├── encrypt/ │ │ │ │ │ │ ├── Encryption.java │ │ │ │ │ │ ├── Encryptions.java │ │ │ │ │ │ └── NoEncryption.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── AlarmEvent.java │ │ │ │ │ │ ├── EnableExceptionPropagation.java │ │ │ │ │ │ ├── EventManager.java │ │ │ │ │ │ ├── SimpleEventBus.java │ │ │ │ │ │ ├── SimpleSubscriber.java │ │ │ │ │ │ └── SubscriberOrder.java │ │ │ │ │ ├── exceptions/ │ │ │ │ │ │ └── ServiceCombException.java │ │ │ │ │ ├── http/ │ │ │ │ │ │ ├── HttpStatus.java │ │ │ │ │ │ ├── HttpStatusManager.java │ │ │ │ │ │ ├── HttpStatusUtils.java │ │ │ │ │ │ └── HttpUtils.java │ │ │ │ │ ├── io/ │ │ │ │ │ │ └── AsyncCloseable.java │ │ │ │ │ ├── log/ │ │ │ │ │ │ └── AbstractMarker.java │ │ │ │ │ ├── net/ │ │ │ │ │ │ ├── IpPort.java │ │ │ │ │ │ ├── NetUtils.java │ │ │ │ │ │ └── URIEndpointObject.java │ │ │ │ │ ├── part/ │ │ │ │ │ │ ├── AbstractPart.java │ │ │ │ │ │ ├── FilePart.java │ │ │ │ │ │ ├── FilePartForSend.java │ │ │ │ │ │ ├── InputStreamPart.java │ │ │ │ │ │ └── ResourcePart.java │ │ │ │ │ ├── spring/ │ │ │ │ │ │ └── PaasNamespaceHandler.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── AbstractRestObjectMapper.java │ │ │ │ │ ├── AsyncUtils.java │ │ │ │ │ ├── BeanUtils.java │ │ │ │ │ ├── ClassLoaderScopeContext.java │ │ │ │ │ ├── ConditionWaiter.java │ │ │ │ │ ├── ExceptionUtils.java │ │ │ │ │ ├── FilePerm.java │ │ │ │ │ ├── FortifyUtils.java │ │ │ │ │ ├── GenericsUtils.java │ │ │ │ │ ├── IOUtils.java │ │ │ │ │ ├── JsonUtils.java │ │ │ │ │ ├── JvmUtils.java │ │ │ │ │ ├── KeyPairEntry.java │ │ │ │ │ ├── KeyPairUtils.java │ │ │ │ │ ├── LambdaMetafactoryUtils.java │ │ │ │ │ ├── LambdaUtils.java │ │ │ │ │ ├── MimeTypesUtils.java │ │ │ │ │ ├── MuteExceptionUtil.java │ │ │ │ │ ├── PartUtils.java │ │ │ │ │ ├── ReflectUtils.java │ │ │ │ │ ├── ResourceUtil.java │ │ │ │ │ ├── RestObjectMapper.java │ │ │ │ │ ├── StringBuilderUtils.java │ │ │ │ │ ├── TimeUtils.java │ │ │ │ │ ├── TypesUtil.java │ │ │ │ │ ├── bean/ │ │ │ │ │ │ ├── ArrayGetter.java │ │ │ │ │ │ ├── ArraySetter.java │ │ │ │ │ │ ├── BoolGetter.java │ │ │ │ │ │ ├── BoolSetter.java │ │ │ │ │ │ ├── ByteGetter.java │ │ │ │ │ │ ├── ByteSetter.java │ │ │ │ │ │ ├── CharGetter.java │ │ │ │ │ │ ├── CharSetter.java │ │ │ │ │ │ ├── DoubleGetter.java │ │ │ │ │ │ ├── DoubleSetter.java │ │ │ │ │ │ ├── FloatGetter.java │ │ │ │ │ │ ├── FloatSetter.java │ │ │ │ │ │ ├── Getter.java │ │ │ │ │ │ ├── IntGetter.java │ │ │ │ │ │ ├── IntSetter.java │ │ │ │ │ │ ├── LongGetter.java │ │ │ │ │ │ ├── LongSetter.java │ │ │ │ │ │ ├── MapGetter.java │ │ │ │ │ │ ├── MapSetter.java │ │ │ │ │ │ ├── Setter.java │ │ │ │ │ │ ├── ShortGetter.java │ │ │ │ │ │ └── ShortSetter.java │ │ │ │ │ └── json/ │ │ │ │ │ ├── JavaxServletPartDeserializer.java │ │ │ │ │ ├── JavaxServletPartSerializer.java │ │ │ │ │ └── PartModule.java │ │ │ │ └── token/ │ │ │ │ └── Keypair4Auth.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── mime.types │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.foundation.common.encrypt.Encryption │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── common/ │ │ │ ├── DynamicObjectTest.java │ │ │ ├── base/ │ │ │ │ └── DynamicEnumTest.java │ │ │ ├── cache/ │ │ │ │ └── TestVersionedCache.java │ │ │ ├── concurrency/ │ │ │ │ └── RunnableWrapperTest.java │ │ │ ├── config/ │ │ │ │ └── BeanProp.java │ │ │ ├── encrypt/ │ │ │ │ └── TestEncryptions.java │ │ │ ├── event/ │ │ │ │ ├── TestEventBus.java │ │ │ │ └── TestEventManager.java │ │ │ ├── http/ │ │ │ │ ├── TestHttpStatus.java │ │ │ │ ├── TestHttpStatusUtils.java │ │ │ │ └── TestHttpUtils.java │ │ │ ├── net/ │ │ │ │ ├── TestIpPort.java │ │ │ │ ├── TestNetUtils.java │ │ │ │ └── TestURIEndpointObject.java │ │ │ ├── part/ │ │ │ │ ├── TestAbstractPart.java │ │ │ │ ├── TestFilePart.java │ │ │ │ ├── TestInputStreamPart.java │ │ │ │ └── TestResourcePart.java │ │ │ ├── spring/ │ │ │ │ └── TestPaasNamespaceHandler.java │ │ │ └── utils/ │ │ │ ├── AsyncUtilsTest.java │ │ │ ├── JsonUtilsTest.java │ │ │ ├── ResourceUtilTest.java │ │ │ ├── TestExceptionUtils.java │ │ │ ├── TestFileNameTooLong.java │ │ │ ├── TestFortifyUtils.java │ │ │ ├── TestGenericsUtils.java │ │ │ ├── TestIOUtils.java │ │ │ ├── TestJvmUtils.java │ │ │ ├── TestLambdaMetafactoryUtils.java │ │ │ ├── TestLambdaPerformance.java │ │ │ ├── TestMimeTypesUtils.java │ │ │ ├── TestRSAUtil.java │ │ │ └── TestTypesUtil.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── spring/ │ │ │ └── config.bean.xml │ │ └── log4j2.xml │ ├── foundation-config/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── config/ │ │ │ │ ├── BootStrapProperties.java │ │ │ │ ├── ConfigEnvironmentPostProcessor.java │ │ │ │ ├── ConfigMapping.java │ │ │ │ ├── ConfigUtil.java │ │ │ │ ├── ConfigurationChangedEvent.java │ │ │ │ ├── DataCenterProperties.java │ │ │ │ ├── DynamicProperties.java │ │ │ │ ├── DynamicPropertiesImpl.java │ │ │ │ ├── DynamicPropertiesSource.java │ │ │ │ ├── FoundationConfigConfiguration.java │ │ │ │ ├── InMemoryDynamicPropertiesSource.java │ │ │ │ ├── YAMLUtil.java │ │ │ │ ├── file/ │ │ │ │ │ ├── AbstractConfigLoader.java │ │ │ │ │ ├── ConfigModel.java │ │ │ │ │ ├── MicroserviceConfigLoader.java │ │ │ │ │ └── YAMLConfigLoader.java │ │ │ │ ├── inject/ │ │ │ │ │ ├── InjectBeanPostProcessor.java │ │ │ │ │ ├── InjectProperties.java │ │ │ │ │ ├── InjectProperty.java │ │ │ │ │ └── PlaceholderResolver.java │ │ │ │ ├── parser/ │ │ │ │ │ ├── Parser.java │ │ │ │ │ ├── PropertiesParser.java │ │ │ │ │ ├── RawParser.java │ │ │ │ │ └── YamlParser.java │ │ │ │ └── priority/ │ │ │ │ ├── ConfigObject.java │ │ │ │ ├── ConfigObjectFactory.java │ │ │ │ ├── ConfigObjectProperty.java │ │ │ │ ├── DynamicProperty.java │ │ │ │ ├── PriorityProperty.java │ │ │ │ ├── PriorityPropertyFactory.java │ │ │ │ ├── PriorityPropertyManager.java │ │ │ │ └── PriorityPropertyType.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ ├── services/ │ │ │ │ │ └── org.apache.servicecomb.config.DynamicPropertiesSource │ │ │ │ ├── spring/ │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ └── spring.factories │ │ │ └── mapping.yaml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── config/ │ │ │ ├── BootStrapPropertiesTest.java │ │ │ ├── DynamicPropertiesTest.java │ │ │ ├── TestConfigMapping.java │ │ │ ├── TestYAMLUtil.java │ │ │ ├── file/ │ │ │ │ └── TestMicroserviceConfigLoader.java │ │ │ ├── inject/ │ │ │ │ ├── TestConfigObjectFactory.java │ │ │ │ └── TestPlaceholderResolver.java │ │ │ ├── parser/ │ │ │ │ └── TestParser.java │ │ │ └── priority/ │ │ │ ├── TestPriorityProperty.java │ │ │ ├── TestPriorityPropertyBase.java │ │ │ ├── TestPriorityPropertyFactory.java │ │ │ └── TestPriorityPropertyManager.java │ │ └── resources/ │ │ ├── empty.yaml │ │ ├── m1.yaml │ │ ├── mapping.yaml │ │ ├── microservice.yaml │ │ ├── test1.yaml │ │ └── test2.yaml │ ├── foundation-metrics/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── metrics/ │ │ │ ├── MetricsBootstrap.java │ │ │ ├── MetricsBootstrapConfig.java │ │ │ ├── MetricsInitializer.java │ │ │ ├── PolledEvent.java │ │ │ ├── health/ │ │ │ │ ├── HealthCheckResult.java │ │ │ │ ├── HealthChecker.java │ │ │ │ └── HealthCheckerManager.java │ │ │ ├── meter/ │ │ │ │ ├── LatencyDistributionConfig.java │ │ │ │ ├── LatencyScopeConfig.java │ │ │ │ └── PeriodMeter.java │ │ │ └── publish/ │ │ │ ├── DefaultTagFinder.java │ │ │ ├── MeasurementGroupConfig.java │ │ │ ├── MeasurementNode.java │ │ │ ├── MeasurementTree.java │ │ │ └── TagFinder.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── foundation/ │ │ └── metrics/ │ │ ├── TestMetricsBootstrap.java │ │ ├── health/ │ │ │ └── TestHealthCheckerManager.java │ │ ├── meter/ │ │ │ └── TestLatencyDistributionConfig.java │ │ └── publish/ │ │ └── spectator/ │ │ ├── TestDefaultTagFinder.java │ │ ├── TestMeasurementGroupConfig.java │ │ ├── TestMeasurementNode.java │ │ ├── TestMeasurementTree.java │ │ └── TestTagFinder.java │ ├── foundation-protobuf/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ ├── io/ │ │ │ │ │ └── protostuff/ │ │ │ │ │ ├── ByteArrayInputEx.java │ │ │ │ │ ├── InputEx.java │ │ │ │ │ ├── OutputEx.java │ │ │ │ │ ├── ProtobufOutputEx.java │ │ │ │ │ ├── SchemaEx.java │ │ │ │ │ ├── SchemaReader.java │ │ │ │ │ ├── SchemaWriter.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── runtime/ │ │ │ │ │ ├── ArrayFieldMapEx.java │ │ │ │ │ ├── FieldMapEx.java │ │ │ │ │ ├── FieldSchema.java │ │ │ │ │ ├── FieldTypeUtils.java │ │ │ │ │ └── HashFieldMapEx.java │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── foundation/ │ │ │ │ └── protobuf/ │ │ │ │ ├── ProtoMapper.java │ │ │ │ ├── ProtoMapperFactory.java │ │ │ │ ├── RootDeserializer.java │ │ │ │ ├── RootSerializer.java │ │ │ │ ├── internal/ │ │ │ │ │ ├── ProtoConst.java │ │ │ │ │ ├── ProtoUtils.java │ │ │ │ │ ├── bean/ │ │ │ │ │ │ ├── BeanDescriptor.java │ │ │ │ │ │ ├── BeanDescriptorManager.java │ │ │ │ │ │ ├── PropertyDescriptor.java │ │ │ │ │ │ └── PropertyWrapper.java │ │ │ │ │ ├── parser/ │ │ │ │ │ │ ├── ContentFileReader.java │ │ │ │ │ │ └── ProtoParser.java │ │ │ │ │ └── schema/ │ │ │ │ │ ├── EnumMeta.java │ │ │ │ │ ├── MessageAsFieldSchema.java │ │ │ │ │ ├── PropertyWrapperAsFieldSchema.java │ │ │ │ │ ├── SchemaManager.java │ │ │ │ │ ├── any/ │ │ │ │ │ │ ├── AnyEntry.java │ │ │ │ │ │ ├── AnyEntrySchema.java │ │ │ │ │ │ └── AnySchema.java │ │ │ │ │ ├── deserializer/ │ │ │ │ │ │ ├── DeserializerSchemaManager.java │ │ │ │ │ │ ├── MessageReadSchema.java │ │ │ │ │ │ ├── repeated/ │ │ │ │ │ │ │ ├── AbstractPrimitiveReaders.java │ │ │ │ │ │ │ ├── AbstractReaders.java │ │ │ │ │ │ │ ├── PrimitiveArrayBuilderWrapper.java │ │ │ │ │ │ │ ├── RepeatedReadSchemas.java │ │ │ │ │ │ │ ├── RepeatedReader.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ ├── AnyRepeatedReadSchemas.java │ │ │ │ │ │ │ ├── BytesRepeatedReadSchemas.java │ │ │ │ │ │ │ ├── MessageRepeatedReadSchemas.java │ │ │ │ │ │ │ ├── PropertyWrapperRepeatedReadSchemas.java │ │ │ │ │ │ │ ├── StringRepeatedReadSchemas.java │ │ │ │ │ │ │ ├── bools/ │ │ │ │ │ │ │ │ ├── BoolRepeatedReadSchemas.java │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ ├── BoolNotPackedReadSchemas.java │ │ │ │ │ │ │ │ └── BoolPackedReadSchemas.java │ │ │ │ │ │ │ ├── doubles/ │ │ │ │ │ │ │ │ ├── DoubleRepeatedReadSchemas.java │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ ├── DoubleNotPackedReadSchemas.java │ │ │ │ │ │ │ │ └── DoublePackedReadSchemas.java │ │ │ │ │ │ │ ├── enums/ │ │ │ │ │ │ │ │ ├── EnumNotPackedReadSchemas.java │ │ │ │ │ │ │ │ ├── EnumPackedReadSchemas.java │ │ │ │ │ │ │ │ └── EnumSchemaUtils.java │ │ │ │ │ │ │ ├── floats/ │ │ │ │ │ │ │ │ ├── FloatRepeatedReadSchemas.java │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ ├── FloatNotPackedReadSchemas.java │ │ │ │ │ │ │ │ └── FloatPackedReadSchemas.java │ │ │ │ │ │ │ ├── ints/ │ │ │ │ │ │ │ │ ├── IntRepeatedReadSchemas.java │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ ├── Fixed32NotPackedReadSchemas.java │ │ │ │ │ │ │ │ ├── Fixed32PackedReadSchemas.java │ │ │ │ │ │ │ │ ├── Int32NotPackedReadSchemas.java │ │ │ │ │ │ │ │ ├── Int32PackedReadSchemas.java │ │ │ │ │ │ │ │ ├── SFixed32NotPackedReadSchemas.java │ │ │ │ │ │ │ │ ├── SFixed32PackedReadSchemas.java │ │ │ │ │ │ │ │ ├── SInt32NotPackedReadSchemas.java │ │ │ │ │ │ │ │ ├── SInt32PackedReadSchemas.java │ │ │ │ │ │ │ │ ├── UInt32NotPackedReadSchemas.java │ │ │ │ │ │ │ │ └── UInt32PackedReadSchemas.java │ │ │ │ │ │ │ └── longs/ │ │ │ │ │ │ │ ├── LongRepeatedReadSchemas.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ ├── Fixed64NotPackedReadSchemas.java │ │ │ │ │ │ │ ├── Fixed64PackedReadSchemas.java │ │ │ │ │ │ │ ├── Int64NotPackedReadSchemas.java │ │ │ │ │ │ │ ├── Int64PackedReadSchemas.java │ │ │ │ │ │ │ ├── SFixed64NotPackedReadSchemas.java │ │ │ │ │ │ │ ├── SFixed64PackedReadSchemas.java │ │ │ │ │ │ │ ├── SInt64NotPackedReadSchemas.java │ │ │ │ │ │ │ ├── SInt64PackedReadSchemas.java │ │ │ │ │ │ │ ├── UInt64NotPackedReadSchemas.java │ │ │ │ │ │ │ └── UInt64PackedReadSchemas.java │ │ │ │ │ │ └── scalar/ │ │ │ │ │ │ ├── AbstractScalarReadSchemas.java │ │ │ │ │ │ ├── BoolReadSchemas.java │ │ │ │ │ │ ├── BytesReadSchemas.java │ │ │ │ │ │ ├── DoubleReadSchemas.java │ │ │ │ │ │ ├── EnumsReadSchemas.java │ │ │ │ │ │ ├── Fixed32ReadSchemas.java │ │ │ │ │ │ ├── Fixed64ReadSchemas.java │ │ │ │ │ │ ├── FloatReadSchemas.java │ │ │ │ │ │ ├── Int32ReadSchemas.java │ │ │ │ │ │ ├── Int64ReadSchemas.java │ │ │ │ │ │ ├── SFixed32ReadSchemas.java │ │ │ │ │ │ ├── SFixed64ReadSchemas.java │ │ │ │ │ │ ├── SInt32ReadSchemas.java │ │ │ │ │ │ ├── SInt64ReadSchemas.java │ │ │ │ │ │ ├── StringReadSchemas.java │ │ │ │ │ │ ├── UInt32ReadSchemas.java │ │ │ │ │ │ └── UInt64ReadSchemas.java │ │ │ │ │ ├── map/ │ │ │ │ │ │ ├── MapEntry.java │ │ │ │ │ │ ├── MapEntrySchema.java │ │ │ │ │ │ └── MapSchema.java │ │ │ │ │ └── serializer/ │ │ │ │ │ ├── MessageWriteSchema.java │ │ │ │ │ ├── SerializerSchemaManager.java │ │ │ │ │ ├── repeated/ │ │ │ │ │ │ ├── AbstractPrimitiveWriters.java │ │ │ │ │ │ ├── AbstractWriters.java │ │ │ │ │ │ ├── RepeatedPrimitiveWriteSchemas.java │ │ │ │ │ │ ├── RepeatedWriteSchemas.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── AnyRepeatedWriteSchemas.java │ │ │ │ │ │ ├── BytesRepeatedWriteSchemas.java │ │ │ │ │ │ ├── MessagesRepeatedWriteSchemas.java │ │ │ │ │ │ ├── PropertyWrapperRepeatedWriteSchemas.java │ │ │ │ │ │ ├── StringsRepeatedWriteSchemas.java │ │ │ │ │ │ ├── bools/ │ │ │ │ │ │ │ ├── BoolNotPackedWriteSchemas.java │ │ │ │ │ │ │ └── BoolPackedWriteSchemas.java │ │ │ │ │ │ ├── doubles/ │ │ │ │ │ │ │ ├── DoubleNotPackedWriteSchemas.java │ │ │ │ │ │ │ └── DoublePackedWriteSchemas.java │ │ │ │ │ │ ├── enums/ │ │ │ │ │ │ │ ├── EnumNotPackedWriteSchemas.java │ │ │ │ │ │ │ └── EnumPackedWriteSchemas.java │ │ │ │ │ │ ├── floats/ │ │ │ │ │ │ │ ├── FloatNotPackedWriteSchemas.java │ │ │ │ │ │ │ └── FloatPackedWriteSchemas.java │ │ │ │ │ │ ├── ints/ │ │ │ │ │ │ │ ├── Fixed32NotPackedWriteSchemas.java │ │ │ │ │ │ │ ├── Fixed32PackedWriteSchemas.java │ │ │ │ │ │ │ ├── Int32NotPackedWriteSchemas.java │ │ │ │ │ │ │ ├── Int32PackedWriteSchemas.java │ │ │ │ │ │ │ ├── SFixed32NotPackedWriteSchemas.java │ │ │ │ │ │ │ ├── SFixed32PackedWriteSchemas.java │ │ │ │ │ │ │ ├── SInt32NotPackedWriteSchemas.java │ │ │ │ │ │ │ ├── SInt32PackedWriteSchemas.java │ │ │ │ │ │ │ ├── UInt32NotPackedWriteSchemas.java │ │ │ │ │ │ │ └── UInt32PackedWriteSchemas.java │ │ │ │ │ │ └── longs/ │ │ │ │ │ │ ├── Fixed64NotPackedWriteSchemas.java │ │ │ │ │ │ ├── Fixed64PackedWriteSchemas.java │ │ │ │ │ │ ├── Int64NotPackedWriteSchemas.java │ │ │ │ │ │ ├── Int64PackedWriteSchemas.java │ │ │ │ │ │ ├── SFixed64NotPackedWriteSchemas.java │ │ │ │ │ │ ├── SFixed64PackedWriteSchemas.java │ │ │ │ │ │ ├── SInt64NotPackedWriteSchemas.java │ │ │ │ │ │ ├── SInt64PackedWriteSchemas.java │ │ │ │ │ │ ├── UInt64NotPackedWriteSchemas.java │ │ │ │ │ │ └── UInt64PackedWriteSchemas.java │ │ │ │ │ └── scalar/ │ │ │ │ │ ├── BoolWriteSchemas.java │ │ │ │ │ ├── BytesWriteSchemas.java │ │ │ │ │ ├── DoubleWriteSchemas.java │ │ │ │ │ ├── EnumWriteSchemas.java │ │ │ │ │ ├── Fixed32WriteSchemas.java │ │ │ │ │ ├── Fixed64WriteSchemas.java │ │ │ │ │ ├── FloatWriteSchemas.java │ │ │ │ │ ├── Int32WriteSchemas.java │ │ │ │ │ ├── Int64WriteSchemas.java │ │ │ │ │ ├── SFixed32WriteSchemas.java │ │ │ │ │ ├── SFixed64WriteSchemas.java │ │ │ │ │ ├── SInt32WriteSchemas.java │ │ │ │ │ ├── SInt64WriteSchemas.java │ │ │ │ │ ├── StringWriteSchemas.java │ │ │ │ │ ├── UInt32WriteSchemas.java │ │ │ │ │ └── UInt64WriteSchemas.java │ │ │ │ └── notice.txt │ │ │ └── resources/ │ │ │ └── google/ │ │ │ └── protobuf/ │ │ │ ├── any.proto │ │ │ └── empty.proto │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── protobuf/ │ │ │ ├── TestISODateTimeParsing.java │ │ │ ├── compatibility/ │ │ │ │ └── TestCompatibilityOfImplementations.java │ │ │ ├── internal/ │ │ │ │ ├── TestModelWrap.java │ │ │ │ ├── TestSchemaBase.java │ │ │ │ ├── bean/ │ │ │ │ │ └── TestBeanDescriptorManager.java │ │ │ │ ├── model/ │ │ │ │ │ ├── CustomGeneric.java │ │ │ │ │ ├── PrimitiveArrays.java │ │ │ │ │ ├── PrimitiveWrapperArrays.java │ │ │ │ │ ├── Root.java │ │ │ │ │ └── User.java │ │ │ │ ├── parser/ │ │ │ │ │ └── TestProtoParser.java │ │ │ │ └── schema/ │ │ │ │ ├── TestAnySchema.java │ │ │ │ ├── TestMapSchema.java │ │ │ │ ├── TestMessageSchema.java │ │ │ │ ├── TestRepeatedSchema.java │ │ │ │ └── scalar/ │ │ │ │ ├── TestBoolSchema.java │ │ │ │ ├── TestBytesSchema.java │ │ │ │ ├── TestDoubleSchema.java │ │ │ │ ├── TestEnumSchema.java │ │ │ │ ├── TestFixed32Schema.java │ │ │ │ ├── TestFixed64Schema.java │ │ │ │ ├── TestFloatSchema.java │ │ │ │ ├── TestInt32Schema.java │ │ │ │ ├── TestInt64Schema.java │ │ │ │ ├── TestNumberBaseSchema.java │ │ │ │ ├── TestSFixed32Schema.java │ │ │ │ ├── TestSFixed64Schema.java │ │ │ │ ├── TestSInt32Schema.java │ │ │ │ ├── TestSInt64Schema.java │ │ │ │ ├── TestStringSchema.java │ │ │ │ ├── TestUInt32Schema.java │ │ │ │ └── TestUInt64Schema.java │ │ │ └── performance/ │ │ │ ├── ProtubufCodecEngine.java │ │ │ ├── TestBase.java │ │ │ ├── TestEngineResult.java │ │ │ ├── TestProtoPerformance.java │ │ │ ├── TestResult.java │ │ │ ├── cases/ │ │ │ │ ├── Empty.java │ │ │ │ ├── Map.java │ │ │ │ ├── Mixed.java │ │ │ │ ├── Pojo.java │ │ │ │ ├── PojoList.java │ │ │ │ ├── Scalars.java │ │ │ │ └── SimpleList.java │ │ │ └── engine/ │ │ │ ├── Jackson.java │ │ │ ├── Protobuf.java │ │ │ ├── Protostuff.java │ │ │ ├── SCB.java │ │ │ ├── ScbStrong.java │ │ │ └── ScbWeak.java │ │ └── resources/ │ │ ├── jacksonRoot.proto │ │ ├── method.proto │ │ ├── model.proto │ │ └── protobufRoot.proto │ ├── foundation-registry/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── registry/ │ │ │ │ ├── DiscoveryManager.java │ │ │ │ ├── RegistrationId.java │ │ │ │ ├── RegistrationManager.java │ │ │ │ ├── RegistryConfiguration.java │ │ │ │ ├── api/ │ │ │ │ │ ├── AbstractDiscoveryInstance.java │ │ │ │ │ ├── DataCenterInfo.java │ │ │ │ │ ├── Discovery.java │ │ │ │ │ ├── DiscoveryInstance.java │ │ │ │ │ ├── LifeCycle.java │ │ │ │ │ ├── MicroserviceInstance.java │ │ │ │ │ ├── MicroserviceInstanceStatus.java │ │ │ │ │ ├── MicroserviceKey.java │ │ │ │ │ ├── PropertyExtended.java │ │ │ │ │ ├── Registration.java │ │ │ │ │ └── RegistrationInstance.java │ │ │ │ ├── definition/ │ │ │ │ │ ├── DefinitionConst.java │ │ │ │ │ └── MicroserviceNameParser.java │ │ │ │ └── discovery/ │ │ │ │ ├── AbstractDiscoveryFilter.java │ │ │ │ ├── AbstractEndpointDiscoveryFilter.java │ │ │ │ ├── AbstractGroupDiscoveryFilter.java │ │ │ │ ├── DiscoveryContext.java │ │ │ │ ├── DiscoveryFilter.java │ │ │ │ ├── DiscoveryTree.java │ │ │ │ ├── DiscoveryTreeNode.java │ │ │ │ ├── InstancePing.java │ │ │ │ ├── InstanceStatusDiscoveryFilter.java │ │ │ │ ├── MicroserviceInstanceCache.java │ │ │ │ ├── StatefulDiscoveryInstance.java │ │ │ │ └── TelnetInstancePing.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.registry.consumer.MicroserviceInstancePing │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── registry/ │ │ │ ├── TestDiscoveryManager.java │ │ │ └── discovery/ │ │ │ ├── TestAbstractDiscoveryFilter.java │ │ │ ├── TestAbstractTransportDiscoveryFilter.java │ │ │ ├── TestDiscoveryContext.java │ │ │ ├── TestDiscoveryTree.java │ │ │ ├── TestDiscoveryTreeNode.java │ │ │ └── TestInstanceStatusDiscoveryFilter.java │ │ └── resources/ │ │ └── log4j2.xml │ ├── foundation-spi/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ ├── auth/ │ │ │ │ ├── AuthHeaderLoader.java │ │ │ │ ├── AuthHeaderProvider.java │ │ │ │ └── SignRequest.java │ │ │ ├── bootstrap/ │ │ │ │ └── BootStrapService.java │ │ │ └── common/ │ │ │ └── utils/ │ │ │ ├── SPIEnabled.java │ │ │ ├── SPIOrder.java │ │ │ └── SPIServiceUtils.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── common/ │ │ │ └── utils/ │ │ │ ├── SPIServiceDef.java │ │ │ ├── SPIServiceDef0.java │ │ │ ├── SPIServiceDef0Impl.java │ │ │ ├── SPIServiceDefImpl.java │ │ │ └── TestSPIServiceUtils.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.foundation.common.utils.SPIServiceDef │ ├── foundation-ssl/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── ssl/ │ │ │ ├── CertificateUtil.java │ │ │ ├── ClientAuth.java │ │ │ ├── KeyStoreUtil.java │ │ │ ├── SSLCustom.java │ │ │ ├── SSLManager.java │ │ │ ├── SSLOption.java │ │ │ ├── SSLOptionFactory.java │ │ │ ├── SSLSocketFactoryExt.java │ │ │ ├── TrustAllManager.java │ │ │ └── TrustManagerExt.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── ssl/ │ │ │ ├── CertificateUtilTest.java │ │ │ ├── KeyStoreUtilTest.java │ │ │ ├── MyOptionFactory.java │ │ │ ├── SSLManagerTest.java │ │ │ ├── SSLOptionTest.java │ │ │ ├── TestSSLOptionFactory.java │ │ │ ├── TestSSLSocketFactoryExt.java │ │ │ ├── TestTrustAllManager.java │ │ │ └── TrustManagerExtTest.java │ │ └── resources/ │ │ ├── client.ssl.properties │ │ ├── microservice.yaml │ │ ├── server.ssl.properties │ │ ├── server.ssl.resource.properties │ │ └── ssl/ │ │ ├── server.cer │ │ ├── server.p12 │ │ ├── trust.cer │ │ ├── trust.jks │ │ └── white.list │ ├── foundation-test-scaffolding/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── foundation/ │ │ └── test/ │ │ └── scaffolding/ │ │ ├── AssertUtils.java │ │ ├── exception/ │ │ │ └── RuntimeExceptionWithoutStackTrace.java │ │ ├── log/ │ │ │ └── LogCollector.java │ │ ├── model/ │ │ │ ├── Color.java │ │ │ ├── Empty.java │ │ │ ├── Media.java │ │ │ ├── People.java │ │ │ └── User.java │ │ ├── spring/ │ │ │ └── SpringUtils.java │ │ └── time/ │ │ ├── MockClock.java │ │ ├── MockTicker.java │ │ └── MockValues.java │ ├── foundation-vertx/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── foundation/ │ │ │ └── vertx/ │ │ │ ├── AddressResolverConfig.java │ │ │ ├── AsyncResultCallback.java │ │ │ ├── ConnectionEvent.java │ │ │ ├── SharedVertxFactory.java │ │ │ ├── SimpleBodyHandler.java │ │ │ ├── SimpleJsonObject.java │ │ │ ├── TransportType.java │ │ │ ├── VertxConst.java │ │ │ ├── VertxTLSBuilder.java │ │ │ ├── VertxUtils.java │ │ │ ├── client/ │ │ │ │ ├── ClientPoolFactory.java │ │ │ │ ├── ClientPoolManager.java │ │ │ │ ├── ClientVerticle.java │ │ │ │ ├── http/ │ │ │ │ │ ├── HttpClientOptionsSPI.java │ │ │ │ │ ├── HttpClientPoolFactory.java │ │ │ │ │ ├── HttpClientWithContext.java │ │ │ │ │ └── HttpClients.java │ │ │ │ └── tcp/ │ │ │ │ ├── AbstractTcpClientConnectionPool.java │ │ │ │ ├── AbstractTcpClientPackage.java │ │ │ │ ├── AbstractTcpClientPoolFactory.java │ │ │ │ ├── NetClientWrapper.java │ │ │ │ ├── TcpClientConfig.java │ │ │ │ ├── TcpClientConnection.java │ │ │ │ ├── TcpClientConnectionPool.java │ │ │ │ ├── TcpClientPackage.java │ │ │ │ ├── TcpClientPoolFactory.java │ │ │ │ ├── TcpData.java │ │ │ │ ├── TcpRequest.java │ │ │ │ └── TcpResponseCallback.java │ │ │ ├── executor/ │ │ │ │ ├── SinglePoolBlockingExecutor.java │ │ │ │ ├── VertxContextExecutor.java │ │ │ │ └── VertxWorkerExecutor.java │ │ │ ├── http/ │ │ │ │ ├── AbstractHttpServletRequest.java │ │ │ │ ├── AbstractHttpServletResponse.java │ │ │ │ ├── BodyBufferSupport.java │ │ │ │ ├── BodyBufferSupportImpl.java │ │ │ │ ├── DownloadUtils.java │ │ │ │ ├── EmptyAsyncContext.java │ │ │ │ ├── FileUploadPart.java │ │ │ │ ├── HttpServletRequestEx.java │ │ │ │ ├── HttpServletResponseEx.java │ │ │ │ ├── ReadStreamPart.java │ │ │ │ ├── StandardHttpServletRequestEx.java │ │ │ │ ├── StandardHttpServletResponseEx.java │ │ │ │ ├── VertxClientRequestToHttpServletRequest.java │ │ │ │ ├── VertxClientResponseToHttpServletResponse.java │ │ │ │ ├── VertxServerRequestToHttpServletRequest.java │ │ │ │ └── VertxServerResponseToHttpServletResponse.java │ │ │ ├── metrics/ │ │ │ │ ├── DefaultClientMetrics.java │ │ │ │ ├── DefaultHttpClientMetrics.java │ │ │ │ ├── DefaultHttpServerMetrics.java │ │ │ │ ├── DefaultTcpClientMetrics.java │ │ │ │ ├── DefaultTcpServerMetrics.java │ │ │ │ ├── DefaultVertxMetrics.java │ │ │ │ ├── DefaultVertxMetricsFactory.java │ │ │ │ ├── MetricsOptionsEx.java │ │ │ │ └── metric/ │ │ │ │ ├── DefaultClientEndpointMetric.java │ │ │ │ ├── DefaultClientEndpointMetricManager.java │ │ │ │ ├── DefaultClientTaskMetric.java │ │ │ │ ├── DefaultEndpointMetric.java │ │ │ │ ├── DefaultRequestMetric.java │ │ │ │ ├── DefaultServerEndpointMetric.java │ │ │ │ └── DefaultTcpSocketMetric.java │ │ │ ├── server/ │ │ │ │ ├── TcpBufferHandler.java │ │ │ │ ├── TcpParser.java │ │ │ │ ├── TcpServer.java │ │ │ │ └── TcpServerConnection.java │ │ │ ├── stream/ │ │ │ │ ├── BufferInputStream.java │ │ │ │ ├── BufferOutputStream.java │ │ │ │ ├── InputStreamToReadStream.java │ │ │ │ ├── OutputStreamToWriteStream.java │ │ │ │ ├── PumpCommon.java │ │ │ │ └── PumpFromPart.java │ │ │ └── tcp/ │ │ │ ├── TcpConnection.java │ │ │ ├── TcpConst.java │ │ │ └── TcpOutputStream.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── foundation/ │ │ └── vertx/ │ │ ├── TestSharedVertxFactory.java │ │ ├── TestSimpleBodyHandler.java │ │ ├── TestSimpleJsonObject.java │ │ ├── TestStream.java │ │ ├── TestVertxTLSBuilder.java │ │ ├── TestVertxUtils.java │ │ ├── client/ │ │ │ ├── TestClientPoolManager.java │ │ │ ├── TestClientVerticle.java │ │ │ └── tcp/ │ │ │ ├── TestAbstractTcpClientPoolFactory.java │ │ │ ├── TestTcpClientConfig.java │ │ │ └── TestTcpClientConnection.java │ │ ├── http/ │ │ │ ├── TestAbstractHttpServletRequest.java │ │ │ ├── TestAbstractHttpServletResponse.java │ │ │ ├── TestBodyBufferSupportImpl.java │ │ │ ├── TestFileUploadPart.java │ │ │ ├── TestStandardHttpServletRequestEx.java │ │ │ ├── TestStandardHttpServletResponseEx.java │ │ │ ├── TestVertxClientRequestToHttpServletRequest.java │ │ │ ├── TestVertxClientResponseToHttpServletResponse.java │ │ │ └── TestVertxServerRequestToHttpServletRequest.java │ │ ├── metrics/ │ │ │ ├── TestDefaultHttpClientMetrics.java │ │ │ ├── TestDefaultHttpServerMetrics.java │ │ │ ├── TestDefaultTcpClientMetrics.java │ │ │ ├── TestDefaultTcpServerMetrics.java │ │ │ ├── TestDefaultVertxMetricsFactory.java │ │ │ └── TestMetricsOptionsEx.java │ │ ├── server/ │ │ │ ├── TestTcpParser.java │ │ │ └── TestTcpServerConnection.java │ │ └── stream/ │ │ └── TestBufferInputStream.java │ └── pom.xml ├── governance/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ ├── governance/ │ │ │ │ ├── GovernanceCommonConfiguration.java │ │ │ │ ├── MatchersManager.java │ │ │ │ ├── MicroserviceMeta.java │ │ │ │ ├── entity/ │ │ │ │ │ └── Configurable.java │ │ │ │ ├── event/ │ │ │ │ │ ├── GovernanceConfigurationChangedEvent.java │ │ │ │ │ └── GovernanceEventManager.java │ │ │ │ ├── exception/ │ │ │ │ │ └── IllegalArgsOperatorException.java │ │ │ │ ├── handler/ │ │ │ │ │ ├── AbstractGovernanceHandler.java │ │ │ │ │ ├── BulkheadHandler.java │ │ │ │ │ ├── CircuitBreakerHandler.java │ │ │ │ │ ├── Disposable.java │ │ │ │ │ ├── DisposableBulkhead.java │ │ │ │ │ ├── DisposableCircuitBreaker.java │ │ │ │ │ ├── DisposableHolder.java │ │ │ │ │ ├── DisposableMap.java │ │ │ │ │ ├── DisposableRateLimiter.java │ │ │ │ │ ├── DisposableRetry.java │ │ │ │ │ ├── DisposableTimeLimiter.java │ │ │ │ │ ├── FaultInjectionHandler.java │ │ │ │ │ ├── GovernanceCacheHandler.java │ │ │ │ │ ├── IdentifierRateLimitingHandler.java │ │ │ │ │ ├── InstanceBulkheadHandler.java │ │ │ │ │ ├── InstanceIsolationHandler.java │ │ │ │ │ ├── LoadBalanceHandler.java │ │ │ │ │ ├── MapperHandler.java │ │ │ │ │ ├── RateLimitingHandler.java │ │ │ │ │ ├── RetryHandler.java │ │ │ │ │ ├── TimeLimiterHandler.java │ │ │ │ │ └── ext/ │ │ │ │ │ ├── AbstractCircuitBreakerExtension.java │ │ │ │ │ ├── AbstractFailurePredictor.java │ │ │ │ │ ├── AbstractInstanceIsolationExtension.java │ │ │ │ │ ├── AbstractRetryExtension.java │ │ │ │ │ ├── ClientRecoverPolicy.java │ │ │ │ │ ├── FailurePredictor.java │ │ │ │ │ └── ServerRecoverPolicy.java │ │ │ │ ├── marker/ │ │ │ │ │ ├── CustomMatcher.java │ │ │ │ │ ├── GovernanceRequest.java │ │ │ │ │ ├── GovernanceRequestExtractor.java │ │ │ │ │ ├── Matcher.java │ │ │ │ │ ├── RequestProcessor.java │ │ │ │ │ ├── TrafficMarker.java │ │ │ │ │ └── operator/ │ │ │ │ │ ├── CompareOperator.java │ │ │ │ │ ├── ContainsOperator.java │ │ │ │ │ ├── ExactOperator.java │ │ │ │ │ ├── MatchOperator.java │ │ │ │ │ ├── PrefixOperator.java │ │ │ │ │ ├── RawOperator.java │ │ │ │ │ └── SuffixOperator.java │ │ │ │ ├── policy/ │ │ │ │ │ ├── AbstractPolicy.java │ │ │ │ │ ├── BulkheadPolicy.java │ │ │ │ │ ├── CircuitBreakerPolicy.java │ │ │ │ │ ├── FaultInjectionPolicy.java │ │ │ │ │ ├── GovernanceCachePolicy.java │ │ │ │ │ ├── IdentifierRateLimitingPolicy.java │ │ │ │ │ ├── LoadBalancerPolicy.java │ │ │ │ │ ├── MapperPolicy.java │ │ │ │ │ ├── RateLimitingPolicy.java │ │ │ │ │ ├── RetryPolicy.java │ │ │ │ │ └── TimeLimiterPolicy.java │ │ │ │ ├── processor/ │ │ │ │ │ ├── injection/ │ │ │ │ │ │ ├── AbortFault.java │ │ │ │ │ │ ├── AbstractFault.java │ │ │ │ │ │ ├── DelayFault.java │ │ │ │ │ │ ├── Fault.java │ │ │ │ │ │ ├── FaultInjectionConst.java │ │ │ │ │ │ ├── FaultInjectionDecorators.java │ │ │ │ │ │ ├── FaultInjectionException.java │ │ │ │ │ │ ├── FaultInjectionUtil.java │ │ │ │ │ │ ├── FaultParam.java │ │ │ │ │ │ ├── FaultResponse.java │ │ │ │ │ │ └── Sleepable.java │ │ │ │ │ ├── loadbanlance/ │ │ │ │ │ │ ├── LoadBalance.java │ │ │ │ │ │ └── LoadBalanceImpl.java │ │ │ │ │ └── mapping/ │ │ │ │ │ └── Mapper.java │ │ │ │ ├── properties/ │ │ │ │ │ ├── BulkheadProperties.java │ │ │ │ │ ├── CircuitBreakerProperties.java │ │ │ │ │ ├── FaultInjectionProperties.java │ │ │ │ │ ├── GovernanceCacheProperties.java │ │ │ │ │ ├── GovernanceProperties.java │ │ │ │ │ ├── IdentifierRateLimitProperties.java │ │ │ │ │ ├── InstanceBulkheadProperties.java │ │ │ │ │ ├── InstanceIsolationProperties.java │ │ │ │ │ ├── LoadBalanceProperties.java │ │ │ │ │ ├── MapperProperties.java │ │ │ │ │ ├── MatchProperties.java │ │ │ │ │ ├── PolicyProperties.java │ │ │ │ │ ├── RateLimitProperties.java │ │ │ │ │ ├── RetryProperties.java │ │ │ │ │ └── TimeLimiterProperties.java │ │ │ │ ├── service/ │ │ │ │ │ ├── GovernanceCache.java │ │ │ │ │ ├── GovernanceCacheImpl.java │ │ │ │ │ ├── MatchersService.java │ │ │ │ │ └── MatchersServiceImpl.java │ │ │ │ └── utils/ │ │ │ │ ├── CustomMatch.java │ │ │ │ └── GovernanceUtils.java │ │ │ └── router/ │ │ │ ├── RouterCommonConfiguration.java │ │ │ ├── RouterFilter.java │ │ │ ├── cache/ │ │ │ │ └── RouterRuleCache.java │ │ │ ├── distribute/ │ │ │ │ ├── AbstractRouterDistributor.java │ │ │ │ └── RouterDistributor.java │ │ │ ├── exception/ │ │ │ │ └── RouterIllegalParamException.java │ │ │ ├── match/ │ │ │ │ └── RouterRuleMatcher.java │ │ │ ├── model/ │ │ │ │ ├── HeaderRule.java │ │ │ │ ├── PolicyRuleItem.java │ │ │ │ ├── RouteItem.java │ │ │ │ ├── ServiceInfoCache.java │ │ │ │ └── TagItem.java │ │ │ └── util/ │ │ │ └── VersionCompareUtil.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ ├── governance/ │ │ │ ├── AbstractFailurePredictorTest.java │ │ │ ├── BulkheadHandlerTest.java │ │ │ ├── CustomMatchTest.java │ │ │ ├── FaultInjectionTest.java │ │ │ ├── FlowControlTest.java │ │ │ ├── GovernanceCacheHandlerTest.java │ │ │ ├── GovernancePropertiesTest.java │ │ │ ├── IdentifierRateLimitingHandlerTest.java │ │ │ ├── InstanceBulkheadHandlerTest.java │ │ │ ├── InstanceIsolationTest.java │ │ │ ├── LoadBalancerTest.java │ │ │ ├── MapperTest.java │ │ │ ├── MockCircuitBreakerExtension.java │ │ │ ├── MockConfiguration.java │ │ │ ├── MockInstanceIsolationExtension.java │ │ │ ├── MockMicroserviceMeta.java │ │ │ ├── MockRetryExtension.java │ │ │ ├── OperatorTest.java │ │ │ ├── RetryHandlerTest.java │ │ │ ├── TimeLimiterHandlerTest.java │ │ │ ├── handler/ │ │ │ │ └── ext/ │ │ │ │ └── RetryExtensionTest.java │ │ │ └── mockclasses/ │ │ │ ├── ClassNotImplements.java │ │ │ ├── ClassWithAnnotation.java │ │ │ ├── CustomMatchDemo.java │ │ │ └── service/ │ │ │ ├── MockConfigurationForCustomMatcher.java │ │ │ └── MockProfileClassUserService.java │ │ └── router/ │ │ ├── ExampleDistributor.java │ │ ├── RouterDistributorDynamicConfig2Test.java │ │ ├── RouterDistributorDynamicConfigTest.java │ │ ├── RouterDistributorFileConfigTest.java │ │ ├── RouterDistributorFileWeightLessTest.java │ │ ├── RouterDistributorFileWithFallbackTest.java │ │ ├── RouterDistributorGlobalConfigTest.java │ │ ├── ServiceIns.java │ │ └── VersionCompareUtilTest.java │ └── resources/ │ ├── META-INF/ │ │ └── spring/ │ │ └── bean.xml │ └── application.yaml ├── handlers/ │ ├── handler-fault-injection/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── faultinjection/ │ │ │ │ ├── ConsumerAbortFaultFilter.java │ │ │ │ ├── ConsumerDelayFaultFilter.java │ │ │ │ ├── FaultInjectionConfiguration.java │ │ │ │ ├── FaultInjectionConst.java │ │ │ │ ├── FaultInjectionUtil.java │ │ │ │ └── FaultParam.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── faultinjection/ │ │ ├── TestConsumerAbortFaultFilter.java │ │ ├── TestConsumerDelayFaultFilter.java │ │ └── TestFaultInjectUtil.java │ ├── handler-flowcontrol-qps/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── qps/ │ │ │ │ ├── Config.java │ │ │ │ ├── ConsumerFlowControlFilter.java │ │ │ │ ├── FlowControlQpsConfiguration.java │ │ │ │ ├── ProviderFlowControlFilter.java │ │ │ │ ├── QpsConst.java │ │ │ │ ├── QpsControllerManager.java │ │ │ │ ├── QpsStrategy.java │ │ │ │ └── strategy/ │ │ │ │ ├── AbstractQpsStrategy.java │ │ │ │ ├── DefaultStrategyFactory.java │ │ │ │ ├── FixedWindowStrategy.java │ │ │ │ ├── IStrategyFactory.java │ │ │ │ ├── LeakyBucketStrategy.java │ │ │ │ └── TokenBucketStrategy.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.qps.strategy.IStrategyFactory │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── qps/ │ │ ├── QpsControllerManagerTest.java │ │ └── TestQpsStrategy.java │ ├── handler-governance/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── handler/ │ │ │ └── governance/ │ │ │ ├── ConsumerInstanceBulkheadFilter.java │ │ │ ├── ConsumerInstanceIsolationFilter.java │ │ │ ├── HandlerGovernanceConfiguration.java │ │ │ ├── ProviderBulkheadFilter.java │ │ │ ├── ProviderCircuitBreakerFilter.java │ │ │ └── ProviderRateLimitingFilter.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── handler-loadbalance/ │ │ ├── pom.xml │ │ ├── readme.MD │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── loadbalance/ │ │ │ │ ├── Configuration.java │ │ │ │ ├── ExtensionsFactory.java │ │ │ │ ├── ExtensionsManager.java │ │ │ │ ├── LoadBalanceConfiguration.java │ │ │ │ ├── LoadBalanceFilter.java │ │ │ │ ├── LoadBalancer.java │ │ │ │ ├── RandomRuleExt.java │ │ │ │ ├── RoundRobinRuleExt.java │ │ │ │ ├── RuleExt.java │ │ │ │ ├── RuleNameExtensionsFactory.java │ │ │ │ ├── ServerListFilterExt.java │ │ │ │ ├── ServerMetrics.java │ │ │ │ ├── ServiceCombServer.java │ │ │ │ ├── SessionStickinessRule.java │ │ │ │ ├── WeightedResponseTimeRuleExt.java │ │ │ │ ├── exception/ │ │ │ │ │ └── LoadbalanceExceptionUtils.java │ │ │ │ └── filter/ │ │ │ │ ├── InstancePropertyDiscoveryFilter.java │ │ │ │ ├── PriorityInstancePropertyDiscoveryFilter.java │ │ │ │ ├── ServerDiscoveryFilter.java │ │ │ │ └── ZoneAwareDiscoveryFilter.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── loadbalance/ │ │ │ ├── MyServerListFilterExt.java │ │ │ ├── TestConfiguration.java │ │ │ ├── TestExtensionsManager.java │ │ │ ├── TestLoadBalanceCreator.java │ │ │ ├── TestLoadBalanceFilter.java │ │ │ ├── TestLoadBalanceFilter2.java │ │ │ ├── TestLoadBalancer.java │ │ │ ├── TestRoundRobinRuleExt.java │ │ │ ├── TestServiceCombServer.java │ │ │ ├── TestSessionStickinessRule.java │ │ │ ├── TestWeightedResponseTimeRuleExt.java │ │ │ ├── exception/ │ │ │ │ └── TestLoadbalanceExceptionUtils.java │ │ │ └── filter/ │ │ │ ├── PriorityInstancePropertyDiscoveryFilterTest.java │ │ │ ├── TestInstancePropertyDiscoveryFilter.java │ │ │ ├── TestServerDiscoveryFilter.java │ │ │ └── TestZoneAwareDiscoveryFilter.java │ │ └── resources/ │ │ └── microservice.yaml │ ├── handler-publickey-auth/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── authentication/ │ │ │ │ ├── AuthenticationBootListener.java │ │ │ │ ├── AuthenticationConfiguration.java │ │ │ │ ├── RSAAuthenticationToken.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── ConsumerAuthFilter.java │ │ │ │ │ └── ConsumerTokenManager.java │ │ │ │ └── provider/ │ │ │ │ ├── AccessController.java │ │ │ │ ├── PathCheckUtils.java │ │ │ │ ├── ProviderAuthFilter.java │ │ │ │ └── ProviderTokenManager.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── authentication/ │ │ │ ├── TestAccessController.java │ │ │ ├── TestAuthenticationBootListener.java │ │ │ ├── TestRSAAuthenticationToken.java │ │ │ └── provider/ │ │ │ ├── TestPathCheckUtils.java │ │ │ └── TestProviderTokenManager.java │ │ └── resources/ │ │ └── microservice.yaml │ ├── handler-router/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── router/ │ │ │ └── custom/ │ │ │ ├── RouterServerListFilter.java │ │ │ ├── ServiceCombRouterConfiguration.java │ │ │ └── ServiceCombRouterDistributor.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── services/ │ │ │ └── org.apache.servicecomb.loadbalance.ServerListFilterExt │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── handler-tracing-zipkin/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── tracing/ │ │ │ └── zipkin/ │ │ │ ├── CustomHttpRequestParser.java │ │ │ ├── CustomHttpResponseParser.java │ │ │ ├── HttpClientRequestWrapper.java │ │ │ ├── HttpClientResponseWrapper.java │ │ │ ├── HttpServeRequestWrapper.java │ │ │ ├── HttpServerResponseWrapper.java │ │ │ ├── InvocationAware.java │ │ │ ├── LogSpanHandler.java │ │ │ ├── TracingConfiguration.java │ │ │ └── ZipkinTracingFilter.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── pom.xml ├── huawei-cloud/ │ ├── darklaunch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── darklaunch/ │ │ │ ├── DarklaunchRule.java │ │ │ ├── DarklaunchRuleItem.java │ │ │ ├── DarklaunchRuleItemJson.java │ │ │ ├── DarklaunchRuleJson.java │ │ │ ├── DarklaunchServerListFilter.java │ │ │ ├── PolicyType.java │ │ │ └── oper/ │ │ │ ├── AbstractCondition.java │ │ │ ├── AndCondition.java │ │ │ ├── CaseInsensitiveCondition.java │ │ │ ├── Condition.java │ │ │ ├── ConditionFactory.java │ │ │ ├── EqualCondition.java │ │ │ ├── GreaterCondition.java │ │ │ ├── GreaterOrEqualCondition.java │ │ │ ├── LessCondition.java │ │ │ ├── LessOrEqualCondition.java │ │ │ ├── LikeCondition.java │ │ │ ├── NotEqualCondition.java │ │ │ ├── OrCondition.java │ │ │ └── SupportedType.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.loadbalance.ServerListFilterExt │ ├── dashboard/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── huaweicloud/ │ │ │ │ └── dashboard/ │ │ │ │ └── monitor/ │ │ │ │ ├── DashboardConfiguration.java │ │ │ │ ├── DataFactory.java │ │ │ │ ├── DefaultMonitorDataPublisher.java │ │ │ │ ├── MetricsMonitorDataProvider.java │ │ │ │ ├── MonitorBootListener.java │ │ │ │ ├── MonitorInformationCollector.java │ │ │ │ ├── TransportUtils.java │ │ │ │ ├── data/ │ │ │ │ │ ├── CPUMonitorCalc.java │ │ │ │ │ └── MonitorConstant.java │ │ │ │ └── model/ │ │ │ │ ├── MonitorDataProvider.java │ │ │ │ └── MonitorDataPublisher.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.core.bootup.BootUpInformationCollector │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── huaweicloud/ │ │ │ └── dashboard/ │ │ │ └── monitor/ │ │ │ └── MetricsMonitorDataProviderTest.java │ │ └── resources/ │ │ └── microservice.yaml │ ├── pom.xml │ └── servicestage/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── huaweicloud/ │ │ │ └── servicestage/ │ │ │ ├── AKSKAuthHeaderProvider.java │ │ │ ├── CasEnvConfig.java │ │ │ ├── CasEnvVariablesAdapter.java │ │ │ ├── RBACBootStrapService.java │ │ │ ├── ServiceStageConfiguration.java │ │ │ ├── TokenAuthHeaderProvider.java │ │ │ └── TokenCacheManager.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── services/ │ │ │ ├── org.apache.servicecomb.foundation.auth.AuthHeaderProvider │ │ │ ├── org.apache.servicecomb.foundation.auth.Cipher │ │ │ ├── org.apache.servicecomb.foundation.bootstrap.BootStrapService │ │ │ └── org.apache.servicecomb.serviceregistry.adapter.EnvAdapter │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── huaweicloud/ │ │ └── servicestage/ │ │ ├── TestAKSKAuthHeaderProvider.java │ │ └── TestEnvVariablesAdapter.java │ └── resources/ │ └── log4j2.xml ├── metrics/ │ ├── metrics-core/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── metrics/ │ │ │ │ └── core/ │ │ │ │ ├── InvocationMetersInitializer.java │ │ │ │ ├── MetricsBootListener.java │ │ │ │ ├── MetricsCoreConfiguration.java │ │ │ │ ├── OsMetersInitializer.java │ │ │ │ ├── SimpleMeterRegistryExt.java │ │ │ │ ├── StepDistributionSummaryExt.java │ │ │ │ ├── SystemMetrics.java │ │ │ │ ├── ThreadPoolMetersInitializer.java │ │ │ │ ├── VertxMetersInitializer.java │ │ │ │ ├── meter/ │ │ │ │ │ ├── ConsumerMeters.java │ │ │ │ │ ├── EdgeMeters.java │ │ │ │ │ ├── ProducerMeters.java │ │ │ │ │ ├── ThreadPoolMonitorPublishModelFactory.java │ │ │ │ │ ├── invocation/ │ │ │ │ │ │ ├── AbstractInvocationMeter.java │ │ │ │ │ │ ├── AbstractInvocationMeters.java │ │ │ │ │ │ ├── ConsumerInvocationMeter.java │ │ │ │ │ │ ├── ConsumerInvocationMeters.java │ │ │ │ │ │ ├── EdgeInvocationMeter.java │ │ │ │ │ │ ├── EdgeInvocationMeters.java │ │ │ │ │ │ ├── MeterInvocationConst.java │ │ │ │ │ │ ├── ProducerInvocationMeter.java │ │ │ │ │ │ └── ProducerInvocationMeters.java │ │ │ │ │ ├── os/ │ │ │ │ │ │ ├── NetMeter.java │ │ │ │ │ │ ├── OsMeter.java │ │ │ │ │ │ ├── SystemMeter.java │ │ │ │ │ │ └── net/ │ │ │ │ │ │ ├── InterfaceUsage.java │ │ │ │ │ │ └── NetStat.java │ │ │ │ │ ├── pool/ │ │ │ │ │ │ └── ThreadPoolMeter.java │ │ │ │ │ └── vertx/ │ │ │ │ │ ├── EndpointMeter.java │ │ │ │ │ ├── HttpClientEndpointMeter.java │ │ │ │ │ ├── HttpClientEndpointsMeter.java │ │ │ │ │ ├── ServerEndpointMeter.java │ │ │ │ │ ├── ServerEndpointsMeter.java │ │ │ │ │ └── VertxEndpointsMeter.java │ │ │ │ └── publish/ │ │ │ │ ├── AbstractMeasurementNodeLogPublisher.java │ │ │ │ ├── ClientEndpointsLogPublisher.java │ │ │ │ ├── DefaultLogPublisher.java │ │ │ │ ├── PublishModelFactory.java │ │ │ │ ├── PublishUtils.java │ │ │ │ ├── ServerEndpointsLogPublisher.java │ │ │ │ ├── SlowInvocationLogger.java │ │ │ │ └── model/ │ │ │ │ ├── ConsumerPublishModel.java │ │ │ │ ├── DefaultPublishModel.java │ │ │ │ ├── EdgePublishModel.java │ │ │ │ ├── ProducerPublishModel.java │ │ │ │ ├── ThreadPoolPublishModel.java │ │ │ │ └── invocation/ │ │ │ │ ├── OperationPerf.java │ │ │ │ ├── OperationPerfGroup.java │ │ │ │ ├── OperationPerfGroups.java │ │ │ │ └── PerfInfo.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── metrics/ │ │ │ └── core/ │ │ │ ├── AssertUtil.java │ │ │ ├── TestInvocationMetersInitializer.java │ │ │ ├── TestOsMeterInitializer.java │ │ │ ├── TestThreadPoolMetersInitializer.java │ │ │ ├── TestVertxMetersInitializer.java │ │ │ ├── meter/ │ │ │ │ └── os/ │ │ │ │ ├── TestNetMeter.java │ │ │ │ └── TestSystemMeter.java │ │ │ └── publish/ │ │ │ ├── TestDefaultLogPublisher.java │ │ │ ├── TestInvocationPublishModelFactory.java │ │ │ ├── TestPublishUtils.java │ │ │ ├── TestSlowInvocationLogger.java │ │ │ ├── TestThreadPoolPublishModelFactory.java │ │ │ └── model/ │ │ │ └── invocation/ │ │ │ ├── TestOperationPerf.java │ │ │ ├── TestOperationPerfGroup.java │ │ │ ├── TestPerfInfo.java │ │ │ └── Utils.java │ │ └── resources/ │ │ └── log4j2.xml │ ├── metrics-integration/ │ │ ├── metrics-prometheus/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── metrics/ │ │ │ │ │ └── prometheus/ │ │ │ │ │ ├── PrometheusConfiguration.java │ │ │ │ │ └── PrometheusPublisher.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ └── spring/ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── metrics/ │ │ │ └── prometheus/ │ │ │ └── TestPrometheusPublisher.java │ │ └── pom.xml │ └── pom.xml ├── parents/ │ ├── default/ │ │ └── pom.xml │ └── pom.xml ├── pom.xml ├── providers/ │ ├── pom.xml │ ├── provider-jaxrs/ │ │ └── pom.xml │ ├── provider-pojo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── provider/ │ │ │ │ └── pojo/ │ │ │ │ ├── DefaultMethodMeta.java │ │ │ │ ├── FilterInvocationCaller.java │ │ │ │ ├── InstanceFactory.java │ │ │ │ ├── InvocationCaller.java │ │ │ │ ├── Invoker.java │ │ │ │ ├── PojoConst.java │ │ │ │ ├── PojoConsumerMetaRefresher.java │ │ │ │ ├── PojoInvocation.java │ │ │ │ ├── PojoInvocationCreator.java │ │ │ │ ├── PojoProducerProvider.java │ │ │ │ ├── ProviderPojoConfiguration.java │ │ │ │ ├── RpcReference.java │ │ │ │ ├── RpcReferenceBeanDefinitionRegistry.java │ │ │ │ ├── RpcSchema.java │ │ │ │ ├── definition/ │ │ │ │ │ ├── PojoConsumerMeta.java │ │ │ │ │ └── PojoConsumerOperationMeta.java │ │ │ │ ├── instance/ │ │ │ │ │ ├── PojoInstanceFactory.java │ │ │ │ │ └── SpringInstanceFactory.java │ │ │ │ ├── reference/ │ │ │ │ │ ├── PojoReferenceMeta.java │ │ │ │ │ ├── ReferenceDefParser.java │ │ │ │ │ └── RpcReferenceProcessor.java │ │ │ │ └── schema/ │ │ │ │ ├── PojoProducerMeta.java │ │ │ │ ├── PojoProducers.java │ │ │ │ └── SchemaDefParser.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.core.ProducerProvider │ │ │ ├── spring/ │ │ │ │ ├── namespace.properties │ │ │ │ ├── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ ├── spring-paas-cse-rpc-1.0.xsd │ │ │ │ └── spring-paas-cse-rpc.xsd │ │ │ ├── spring.handlers │ │ │ └── spring.schemas │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── provider/ │ │ │ └── pojo/ │ │ │ ├── IPerson.java │ │ │ ├── Person.java │ │ │ ├── PersonReference.java │ │ │ ├── instance/ │ │ │ │ ├── TestPojoInstanceFactory.java │ │ │ │ └── TestSpringInstanceFactory.java │ │ │ ├── reference/ │ │ │ │ ├── PojoReferenceMetaTest.java │ │ │ │ └── TestRpcReferenceProcessor.java │ │ │ └── schema/ │ │ │ ├── TestPojoProducers.java │ │ │ └── TestPojoSchemaMeta.java │ │ └── resources/ │ │ └── microservice.yaml │ ├── provider-rest-common/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── provider/ │ │ │ │ └── rest/ │ │ │ │ └── common/ │ │ │ │ ├── InvocationToHttpServletRequest.java │ │ │ │ ├── ProducerHttpRequestArgMapper.java │ │ │ │ ├── ProducerHttpRequestArgMapperFactory.java │ │ │ │ ├── ProducerServerWebSocketArgMapperFactory.java │ │ │ │ ├── ProducerServerWebSocketMapper.java │ │ │ │ ├── ProviderRestCommonConfiguration.java │ │ │ │ ├── RestProducerProvider.java │ │ │ │ ├── RestProducers.java │ │ │ │ └── RestSchema.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── org.apache.servicecomb.core.ProducerProvider │ │ │ │ └── org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── provider/ │ │ └── rest/ │ │ └── common/ │ │ ├── TestInvocationToHttpServletRequest.java │ │ ├── TestProducerHttpRequestArgMapper.java │ │ ├── TestRestProducers.java │ │ └── TestRestServiceProvider.java │ └── provider-springmvc/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── provider/ │ │ └── springmvc/ │ │ └── reference/ │ │ ├── AcceptableRestTemplate.java │ │ ├── CommonToHttpServletRequest.java │ │ ├── CseClientHttpRequest.java │ │ ├── CseClientHttpRequestFactory.java │ │ ├── CseClientHttpResponse.java │ │ ├── CseHttpEntity.java │ │ ├── CseHttpMessageConverter.java │ │ ├── CseHttpMessageConverterExtractor.java │ │ ├── CseRequestCallback.java │ │ ├── CseResponseEntityResponseExtractor.java │ │ ├── CseRestTemplate.java │ │ ├── CseUriTemplateHandler.java │ │ ├── RequestMeta.java │ │ ├── RestTemplateBuilder.java │ │ ├── RestTemplateWrapper.java │ │ ├── ServiceCombRestTemplateConfig.java │ │ ├── UrlWithProviderPrefixClientHttpRequestFactory.java │ │ └── UrlWithServiceNameClientHttpRequestFactory.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── provider/ │ │ └── springmvc/ │ │ └── reference/ │ │ ├── ServiceCombRestTemplateConfigTest.java │ │ ├── TestCommonToHttpServletRequest.java │ │ ├── TestCseClientHttpRequestFactory.java │ │ ├── TestCseClientHttpResponse.java │ │ ├── TestCseHttpEntity.java │ │ ├── TestCseRequestCallback.java │ │ ├── TestCseRestTemplate.java │ │ ├── TestCseUriTemplateHandler.java │ │ ├── TestRequestMeta.java │ │ ├── TestRestTemplateBuilder.java │ │ ├── TestRestTemplateWrapper.java │ │ ├── TestUrlWithProviderPrefixClientHttpRequestFactory.java │ │ └── TestUrlWithServiceNameClientHttpRequestFactory.java │ └── resources/ │ ├── log4j2.xml │ └── microservice.yaml ├── samples/ │ └── README.md ├── service-registry/ │ ├── pom.xml │ ├── registry-consul/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── registry/ │ │ │ └── consul/ │ │ │ ├── ConsulConfiguration.java │ │ │ ├── ConsulConst.java │ │ │ ├── ConsulDiscovery.java │ │ │ ├── ConsulDiscoveryInstance.java │ │ │ ├── ConsulInstance.java │ │ │ ├── ConsulRegistration.java │ │ │ ├── ConsulRegistrationInstance.java │ │ │ └── config/ │ │ │ ├── ConsulDiscoveryProperties.java │ │ │ └── ConsulProperties.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── registry-etcd/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── registry/ │ │ │ └── etcd/ │ │ │ ├── EtcdConfiguration.java │ │ │ ├── EtcdConst.java │ │ │ ├── EtcdDiscovery.java │ │ │ ├── EtcdDiscoveryInstance.java │ │ │ ├── EtcdInstance.java │ │ │ ├── EtcdRegistration.java │ │ │ ├── EtcdRegistrationInstance.java │ │ │ └── EtcdRegistryProperties.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── registry-lightweight/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── registry/ │ │ │ │ └── lightweight/ │ │ │ │ ├── AbstractLightweightDiscovery.java │ │ │ │ ├── AbstractLightweightRegistration.java │ │ │ │ ├── DiscoveryClient.java │ │ │ │ ├── DiscoveryEndpoint.java │ │ │ │ ├── LightWeightRegistryConfiguration.java │ │ │ │ ├── Message.java │ │ │ │ ├── MessageExecutor.java │ │ │ │ ├── MessageType.java │ │ │ │ ├── MicroserviceInfo.java │ │ │ │ ├── RegisterException.java │ │ │ │ ├── RegisterInstanceEvent.java │ │ │ │ ├── RegisterRequest.java │ │ │ │ ├── SchemaChangedEvent.java │ │ │ │ ├── Self.java │ │ │ │ ├── StoreService.java │ │ │ │ ├── UnregisterRequest.java │ │ │ │ ├── model/ │ │ │ │ │ ├── AbstractPropertiesLoader.java │ │ │ │ │ ├── FindInstancesResponse.java │ │ │ │ │ ├── HealthCheck.java │ │ │ │ │ ├── HealthCheckMode.java │ │ │ │ │ ├── InstancePropertiesLoader.java │ │ │ │ │ ├── Microservice.java │ │ │ │ │ ├── MicroserviceFactory.java │ │ │ │ │ ├── MicroserviceInstance.java │ │ │ │ │ ├── MicroserviceInstances.java │ │ │ │ │ └── MicroservicePropertiesLoader.java │ │ │ │ └── store/ │ │ │ │ ├── AppStore.java │ │ │ │ ├── InstanceStore.java │ │ │ │ ├── MicroserviceStore.java │ │ │ │ └── Store.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── registry/ │ │ ├── config/ │ │ │ └── TestAbstractPropertiesLoader.java │ │ └── lightweight/ │ │ ├── MessageTest.java │ │ └── StoreServiceTest.java │ ├── registry-local/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── localregistry/ │ │ │ ├── LocalDiscovery.java │ │ │ ├── LocalDiscoveryInstance.java │ │ │ ├── LocalRegistration.java │ │ │ ├── LocalRegistrationInstance.java │ │ │ ├── LocalRegistryConfiguration.java │ │ │ ├── LocalRegistryConst.java │ │ │ ├── LocalRegistryStore.java │ │ │ └── RegistryBean.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── registry-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── registry/ │ │ │ └── nacos/ │ │ │ ├── NacosConfiguration.java │ │ │ ├── NacosConst.java │ │ │ ├── NacosDiscovery.java │ │ │ ├── NacosDiscoveryInstance.java │ │ │ ├── NacosDiscoveryProperties.java │ │ │ ├── NacosMicroserviceHandler.java │ │ │ ├── NacosRegistration.java │ │ │ ├── NacosRegistrationInstance.java │ │ │ └── NamingServiceManager.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── registry-service-center/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── registry/ │ │ │ └── sc/ │ │ │ ├── MicroserviceHandler.java │ │ │ ├── SCAddressManager.java │ │ │ ├── SCClientUtils.java │ │ │ ├── SCConfiguration.java │ │ │ ├── SCConfigurationProperties.java │ │ │ ├── SCConst.java │ │ │ ├── SCDiscovery.java │ │ │ ├── SCDiscoveryInstance.java │ │ │ ├── SCRegistration.java │ │ │ └── SCRegistrationInstance.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── registry-zero-config/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── zeroconfig/ │ │ │ ├── AbstractZeroConfigRegistration.java │ │ │ ├── Config.java │ │ │ ├── ZeroConfigConst.java │ │ │ ├── ZeroConfigDiscovery.java │ │ │ ├── ZeroConfigDiscoveryInstance.java │ │ │ ├── ZeroConfigRegistration.java │ │ │ ├── ZeroConfigRegistrationInstance.java │ │ │ ├── ZeroConfigRegistryConfiguration.java │ │ │ └── multicast/ │ │ │ ├── Multicast.java │ │ │ └── MulticastServer.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── registry-zookeeper/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── registry/ │ │ └── zookeeper/ │ │ ├── ZookeeperConfiguration.java │ │ ├── ZookeeperConst.java │ │ ├── ZookeeperDiscovery.java │ │ ├── ZookeeperDiscoveryInstance.java │ │ ├── ZookeeperInstance.java │ │ ├── ZookeeperRegistration.java │ │ ├── ZookeeperRegistrationInstance.java │ │ └── ZookeeperRegistryProperties.java │ └── resources/ │ └── META-INF/ │ └── spring/ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports ├── solutions/ │ ├── pom.xml │ └── solution-basic/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── solution/ │ │ │ └── basic/ │ │ │ └── integration/ │ │ │ ├── HealthEndpoint.java │ │ │ ├── HealthEndpointImpl.java │ │ │ ├── HealthInstancePing.java │ │ │ ├── InstanceOpenAPIRegistry.java │ │ │ ├── IntegrationConfiguration.java │ │ │ ├── ManagementEndpoint.java │ │ │ ├── ManagementEndpointImpl.java │ │ │ ├── MetricsEndpoint.java │ │ │ └── MetricsEndpointImpl.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── microservice.yaml │ └── test/ │ └── java/ │ └── org/ │ └── apache/ │ └── servicecomb/ │ └── solution/ │ └── basic/ │ └── integration/ │ ├── TestHealthEndpointImpl.java │ ├── TestManagementEndpointImpl.java │ └── TestMetricsEndpointImpl.java ├── spring-boot/ │ ├── pom.xml │ └── spring-boot-starters/ │ ├── java-chassis-spring-boot-starter-servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── springboot/ │ │ │ └── starter/ │ │ │ └── servlet/ │ │ │ ├── RestServletInitializer.java │ │ │ ├── SpringBootStarterServletConfiguration.java │ │ │ └── package-info.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── java-chassis-spring-boot-starter-standalone/ │ │ └── pom.xml │ └── pom.xml ├── swagger/ │ ├── pom.xml │ ├── swagger-generator/ │ │ ├── generator-core/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── swagger/ │ │ │ │ │ ├── SwaggerUtils.java │ │ │ │ │ ├── converter/ │ │ │ │ │ │ ├── AbstractConverter.java │ │ │ │ │ │ ├── Converter.java │ │ │ │ │ │ ├── ConverterMgr.java │ │ │ │ │ │ └── property/ │ │ │ │ │ │ ├── AbstractPropertyConverter.java │ │ │ │ │ │ ├── ArrayPropertyConverter.java │ │ │ │ │ │ ├── MapPropertyConverter.java │ │ │ │ │ │ └── ObjectPropertyConverter.java │ │ │ │ │ ├── extend/ │ │ │ │ │ │ ├── ConcreteTypeRegister.java │ │ │ │ │ │ ├── DefaultModelResolveObjectMapperProvider.java │ │ │ │ │ │ ├── ModelResolveObjectMapperProvider.java │ │ │ │ │ │ ├── ModelResolverExt.java │ │ │ │ │ │ ├── SwaggerEnum.java │ │ │ │ │ │ ├── annotations/ │ │ │ │ │ │ │ └── RawJsonRequestBody.java │ │ │ │ │ │ ├── introspector/ │ │ │ │ │ │ │ └── JsonPropertyIntrospector.java │ │ │ │ │ │ ├── module/ │ │ │ │ │ │ │ └── EnumModuleExt.java │ │ │ │ │ │ └── property/ │ │ │ │ │ │ └── creator/ │ │ │ │ │ │ ├── ByteArrayPropertyCreator.java │ │ │ │ │ │ ├── BytePropertyCreator.java │ │ │ │ │ │ ├── InputStreamPropertyCreator.java │ │ │ │ │ │ ├── PartPropertyCreator.java │ │ │ │ │ │ └── PropertyCreator.java │ │ │ │ │ └── generator/ │ │ │ │ │ ├── ClassAnnotationProcessor.java │ │ │ │ │ ├── MethodAnnotationProcessor.java │ │ │ │ │ ├── OperationGenerator.java │ │ │ │ │ ├── ParameterAnnotationProcessor.java │ │ │ │ │ ├── ParameterGenerator.java │ │ │ │ │ ├── ParameterTypeProcessor.java │ │ │ │ │ ├── ResponseTypeProcessor.java │ │ │ │ │ ├── SwaggerConst.java │ │ │ │ │ ├── SwaggerContextRegister.java │ │ │ │ │ ├── SwaggerGenerator.java │ │ │ │ │ ├── SwaggerGeneratorFactory.java │ │ │ │ │ ├── SwaggerGeneratorUtils.java │ │ │ │ │ ├── SwaggerParameterAnnotationProcessor.java │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── AbstractOperationGenerator.java │ │ │ │ │ │ ├── AbstractSwaggerGenerator.java │ │ │ │ │ │ ├── OperationGeneratorContext.java │ │ │ │ │ │ ├── ParameterGeneratorContext.java │ │ │ │ │ │ ├── SwaggerGeneratorContext.java │ │ │ │ │ │ ├── model/ │ │ │ │ │ │ │ ├── HttpParameterType.java │ │ │ │ │ │ │ ├── SwaggerOperation.java │ │ │ │ │ │ │ └── SwaggerOperations.java │ │ │ │ │ │ ├── processor/ │ │ │ │ │ │ │ ├── annotation/ │ │ │ │ │ │ │ │ ├── AnnotationUtils.java │ │ │ │ │ │ │ │ ├── ApiResponseMethodProcessor.java │ │ │ │ │ │ │ │ ├── ApiResponsesMethodProcessor.java │ │ │ │ │ │ │ │ ├── OpenAPIDefinitionProcessor.java │ │ │ │ │ │ │ │ ├── OperationMethodAnnotationProcessor.java │ │ │ │ │ │ │ │ ├── RequestBodyMethodAnnotationProcessor.java │ │ │ │ │ │ │ │ └── package-info.java │ │ │ │ │ │ │ ├── parameter/ │ │ │ │ │ │ │ │ ├── HttpServletRequestContextRegister.java │ │ │ │ │ │ │ │ ├── ParameterParameterAnnotationProcessor.java │ │ │ │ │ │ │ │ ├── PartArrayParameterTypeProcessor.java │ │ │ │ │ │ │ │ ├── PartListParameterTypeProcessor.java │ │ │ │ │ │ │ │ ├── PartParameterTypeProcessor.java │ │ │ │ │ │ │ │ ├── RawJsonRequestBodyProcessor.java │ │ │ │ │ │ │ │ ├── RequestBodyParameterAnnotationProcessor.java │ │ │ │ │ │ │ │ └── ServerWebSocketContextRegister.java │ │ │ │ │ │ │ └── response/ │ │ │ │ │ │ │ ├── CompletableFutureProcessor.java │ │ │ │ │ │ │ ├── DefaultResponseTypeProcessor.java │ │ │ │ │ │ │ ├── OptionalProcessor.java │ │ │ │ │ │ │ └── PublisherProcessor.java │ │ │ │ │ │ ├── unittest/ │ │ │ │ │ │ │ ├── UnitTestSwaggerUtils.java │ │ │ │ │ │ │ └── package-info.java │ │ │ │ │ │ └── utils/ │ │ │ │ │ │ └── MethodUtils.java │ │ │ │ │ ├── pojo/ │ │ │ │ │ │ ├── PojoOperationGenerator.java │ │ │ │ │ │ ├── PojoSwaggerGenerator.java │ │ │ │ │ │ └── PojoSwaggerGeneratorFactory.java │ │ │ │ │ └── rest/ │ │ │ │ │ ├── RestOperationGenerator.java │ │ │ │ │ └── RestSwaggerGenerator.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ └── services/ │ │ │ │ ├── io.swagger.v3.core.converter.ModelConverter │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ParameterTypeProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ResponseTypeProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.SwaggerContextRegister │ │ │ │ ├── org.apache.servicecomb.swagger.generator.SwaggerGenerator │ │ │ │ └── org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ ├── TestSwaggerUtils.java │ │ │ │ ├── extend/ │ │ │ │ │ ├── introspector/ │ │ │ │ │ │ └── JsonPropertyIntrospectorTest.java │ │ │ │ │ ├── module/ │ │ │ │ │ │ └── EnumModuleExtTest.java │ │ │ │ │ └── property/ │ │ │ │ │ └── creator/ │ │ │ │ │ └── TestPartPropertyCreator.java │ │ │ │ └── generator/ │ │ │ │ └── core/ │ │ │ │ ├── TestApiOperation.java │ │ │ │ ├── TestApiResponse.java │ │ │ │ ├── TestArrayType.java │ │ │ │ ├── TestClassUtils.java │ │ │ │ ├── TestOperationGenerator.java │ │ │ │ ├── TestPojo.java │ │ │ │ ├── TestPojoExample.java │ │ │ │ ├── TestSwaggerDefinition.java │ │ │ │ ├── TestSwaggerGenerator.java │ │ │ │ ├── TestSwaggerUtils.java │ │ │ │ ├── model/ │ │ │ │ │ └── TestSwaggerOperations.java │ │ │ │ ├── pojo/ │ │ │ │ │ ├── PojoExample1.java │ │ │ │ │ ├── TestType1.java │ │ │ │ │ ├── TestType2.java │ │ │ │ │ ├── TestTypeClass.java │ │ │ │ │ ├── TestTypeEnumLang.java │ │ │ │ │ └── TestTypeRecord.java │ │ │ │ ├── processor/ │ │ │ │ │ └── annotation/ │ │ │ │ │ ├── OpenAPIDefinitionProcessorTest.java │ │ │ │ │ └── OperationMethodAnnotationProcessorTest.java │ │ │ │ ├── schema/ │ │ │ │ │ ├── AllType.java │ │ │ │ │ ├── ArrayType.java │ │ │ │ │ ├── RepeatOperation.java │ │ │ │ │ └── Schema.java │ │ │ │ └── utils/ │ │ │ │ ├── TestMethodUtils.java │ │ │ │ ├── methodUtilsModel/ │ │ │ │ │ ├── AbstractBaseClass.java │ │ │ │ │ ├── AbstractBean.java │ │ │ │ │ ├── BaseInterface.java │ │ │ │ │ ├── Hello2Endpoint.java │ │ │ │ │ ├── HelloBean.java │ │ │ │ │ ├── HelloEndpoint.java │ │ │ │ │ └── ServiceInterface.java │ │ │ │ └── paramUtilsModel/ │ │ │ │ ├── AbstractBaseService.java │ │ │ │ ├── AbstractBean.java │ │ │ │ ├── IBaseService.java │ │ │ │ ├── IMyService.java │ │ │ │ ├── IMyServiceChild.java │ │ │ │ ├── IMyServiceChild2.java │ │ │ │ ├── MyEndpoint.java │ │ │ │ ├── MyEndpoint2.java │ │ │ │ └── PersonBean.java │ │ │ └── resources/ │ │ │ ├── schemas/ │ │ │ │ ├── ParameterAnnotation.yaml │ │ │ │ ├── Schema.yaml │ │ │ │ ├── allMethod.yaml │ │ │ │ ├── allType.yaml │ │ │ │ ├── apiOperation.yaml │ │ │ │ ├── apiResponse.yaml │ │ │ │ ├── array.yaml │ │ │ │ ├── boolean.yaml │ │ │ │ ├── booleanObject.yaml │ │ │ │ ├── byte.yaml │ │ │ │ ├── byteObject.yaml │ │ │ │ ├── bytes.yaml │ │ │ │ ├── bytesObject.yaml │ │ │ │ ├── char.yaml │ │ │ │ ├── charObject.yaml │ │ │ │ ├── completableFuture.yaml │ │ │ │ ├── date.yaml │ │ │ │ ├── double.yaml │ │ │ │ ├── doubleObject.yaml │ │ │ │ ├── enum.yaml │ │ │ │ ├── float.yaml │ │ │ │ ├── floatObject.yaml │ │ │ │ ├── ignoreRequest.yaml │ │ │ │ ├── int.yaml │ │ │ │ ├── intObject.yaml │ │ │ │ ├── list.yaml │ │ │ │ ├── long.yaml │ │ │ │ ├── longObject.yaml │ │ │ │ ├── map.yaml │ │ │ │ ├── mapList.yaml │ │ │ │ ├── multiParam.yaml │ │ │ │ ├── nestedListString.yaml │ │ │ │ ├── object.yaml │ │ │ │ ├── oneEnum.yaml │ │ │ │ ├── part.yaml │ │ │ │ ├── partArray.yaml │ │ │ │ ├── partList.yaml │ │ │ │ ├── pojoExample1.yaml │ │ │ │ ├── responseHeader.yaml │ │ │ │ ├── set.yaml │ │ │ │ ├── short.yaml │ │ │ │ ├── shortObject.yaml │ │ │ │ ├── string.yaml │ │ │ │ ├── testCompletableFutureOptional.yaml │ │ │ │ ├── testOptional.yaml │ │ │ │ └── wrapToBodyWithDesc.yaml │ │ │ ├── swagger1.yaml │ │ │ └── swagger2.yaml │ │ ├── generator-jaxrs/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── swagger/ │ │ │ │ │ └── generator/ │ │ │ │ │ └── jaxrs/ │ │ │ │ │ ├── JaxrsOperationGenerator.java │ │ │ │ │ ├── JaxrsSwaggerGenerator.java │ │ │ │ │ ├── JaxrsSwaggerGeneratorFactory.java │ │ │ │ │ └── processor/ │ │ │ │ │ ├── annotation/ │ │ │ │ │ │ ├── ConsumesClassAnnotationProcessor.java │ │ │ │ │ │ ├── ConsumesMethodAnnotationProcessor.java │ │ │ │ │ │ ├── CookieParamParameterAnnotationProcessor.java │ │ │ │ │ │ ├── DeleteMethodAnnotationProcessor.java │ │ │ │ │ │ ├── FormParamParameterAnnotationProcessor.java │ │ │ │ │ │ ├── GetMethodAnnotationProcessor.java │ │ │ │ │ │ ├── HeaderParamParameterAnnotationProcessor.java │ │ │ │ │ │ ├── JaxrsClassAnnotationProcessor.java │ │ │ │ │ │ ├── JaxrsMethodAnnotationProcessor.java │ │ │ │ │ │ ├── JaxrsParameterAnnotationProcessor.java │ │ │ │ │ │ ├── PatchMethodAnnotationProcessor.java │ │ │ │ │ │ ├── PathClassAnnotationProcessor.java │ │ │ │ │ │ ├── PathMethodAnnotationProcessor.java │ │ │ │ │ │ ├── PathParamParameterAnnotationProcessor.java │ │ │ │ │ │ ├── PostMethodAnnotationProcessor.java │ │ │ │ │ │ ├── ProducesClassAnnotationProcessor.java │ │ │ │ │ │ ├── ProducesMethodAnnotationProcessor.java │ │ │ │ │ │ ├── PutMethodAnnotationProcessor.java │ │ │ │ │ │ └── QueryParamParameterAnnotationProcessor.java │ │ │ │ │ └── response/ │ │ │ │ │ └── JaxrsResponseProcessor.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ └── services/ │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ResponseTypeProcessor │ │ │ │ └── org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ └── generator/ │ │ │ │ └── jaxrs/ │ │ │ │ ├── ClassAnnotation.java │ │ │ │ ├── ClassMethodNoPath.java │ │ │ │ ├── Echo.java │ │ │ │ ├── FullSwaggerService.java │ │ │ │ ├── MultiDefaultPath.java │ │ │ │ ├── TestClassAnnotation.java │ │ │ │ ├── TestJaxrs.java │ │ │ │ └── model/ │ │ │ │ ├── AggregatedParam.java │ │ │ │ ├── BeanParamComplexField.java │ │ │ │ ├── BeanParamComplexSetter.java │ │ │ │ ├── BeanParamDefaultBody.java │ │ │ │ ├── BeanParamInvalidDefaultBody.java │ │ │ │ ├── BeanParamWithJsonIgnoredTagged.java │ │ │ │ ├── BeanParamWithPart.java │ │ │ │ ├── ConsumesAndProduces.java │ │ │ │ └── enums/ │ │ │ │ ├── DynamicStatus.java │ │ │ │ ├── DynamicStatusBeanParam.java │ │ │ │ ├── DynamicStatusModel.java │ │ │ │ ├── JdkStatus.java │ │ │ │ ├── JdkStatusBeanParam.java │ │ │ │ └── JdkStatusModel.java │ │ │ └── resources/ │ │ │ └── schemas/ │ │ │ ├── ClassAnnotation.yaml │ │ │ ├── FullSwaggerService.yaml │ │ │ ├── aggregatedParam.yaml │ │ │ ├── beanParamDefaultBody.yaml │ │ │ ├── beanParamWithJsonIgnoredTagged.yaml │ │ │ ├── beanParamWithPart.yaml │ │ │ ├── consumes.yaml │ │ │ ├── cookie.yaml │ │ │ ├── dynamicStatusEnum.yaml │ │ │ ├── echo.yaml │ │ │ ├── emptyContract.yaml │ │ │ ├── emptyPath.yaml │ │ │ ├── enumBody.yaml │ │ │ ├── form.yaml │ │ │ ├── jdkStatusEnum.yaml │ │ │ ├── nestedListString.yaml │ │ │ ├── patch.yaml │ │ │ ├── query.yaml │ │ │ ├── rawJsonStringMethod.yaml │ │ │ ├── response.yaml │ │ │ └── responseText.yaml │ │ ├── generator-spring-data/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── swagger/ │ │ │ │ │ └── generator/ │ │ │ │ │ └── springdata/ │ │ │ │ │ ├── SpringDataConcreteTypeRegister.java │ │ │ │ │ └── SpringDataModule.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ └── services/ │ │ │ │ ├── com.fasterxml.jackson.databind.Module │ │ │ │ └── org.apache.servicecomb.swagger.extend.ConcreteTypeRegister │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ └── generator/ │ │ │ │ └── springdata/ │ │ │ │ └── TestPageResponseTypeProcessor.java │ │ │ └── resources/ │ │ │ └── pageSchema.yaml │ │ ├── generator-springmvc/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── apache/ │ │ │ │ │ └── servicecomb/ │ │ │ │ │ └── swagger/ │ │ │ │ │ └── generator/ │ │ │ │ │ └── springmvc/ │ │ │ │ │ ├── SpringmvcOperationGenerator.java │ │ │ │ │ ├── SpringmvcSwaggerGenerator.java │ │ │ │ │ ├── SpringmvcSwaggerGeneratorFactory.java │ │ │ │ │ ├── processor/ │ │ │ │ │ │ ├── annotation/ │ │ │ │ │ │ │ ├── AbstractHttpMethodMappingAnnotationProcessor.java │ │ │ │ │ │ │ ├── CookieValueAnnotationProcessor.java │ │ │ │ │ │ │ ├── DeleteMappingMethodAnnotationProcessor.java │ │ │ │ │ │ │ ├── GetMappingMethodAnnotationProcessor.java │ │ │ │ │ │ │ ├── PatchMappingMethodAnnotationProcessor.java │ │ │ │ │ │ │ ├── PathVariableAnnotationProcessor.java │ │ │ │ │ │ │ ├── PostMappingMethodAnnotationProcessor.java │ │ │ │ │ │ │ ├── PutMappingMethodAnnotationProcessor.java │ │ │ │ │ │ │ ├── RequestAttributeAnnotationProcessor.java │ │ │ │ │ │ │ ├── RequestBodyAnnotationProcessor.java │ │ │ │ │ │ │ ├── RequestHeaderAnnotationProcessor.java │ │ │ │ │ │ │ ├── RequestMappingClassAnnotationProcessor.java │ │ │ │ │ │ │ ├── RequestMappingMethodAnnotationProcessor.java │ │ │ │ │ │ │ ├── RequestParamParameterProcessor.java │ │ │ │ │ │ │ ├── RequestPartAnnotationProcessor.java │ │ │ │ │ │ │ ├── RestControllerClassAnnotationProcessor.java │ │ │ │ │ │ │ └── SpringmvcParameterAnnotationsProcessor.java │ │ │ │ │ │ ├── parameter/ │ │ │ │ │ │ │ ├── MultipartFileArrayProcessor.java │ │ │ │ │ │ │ ├── MultipartFileListProcessor.java │ │ │ │ │ │ │ └── MultipartFileTypeProcessor.java │ │ │ │ │ │ └── response/ │ │ │ │ │ │ └── ResponseEntityProcessor.java │ │ │ │ │ └── property/ │ │ │ │ │ └── creator/ │ │ │ │ │ └── MultipartFilePropertyCreator.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ └── services/ │ │ │ │ ├── org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ParameterTypeProcessor │ │ │ │ ├── org.apache.servicecomb.swagger.generator.ResponseTypeProcessor │ │ │ │ └── org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ └── generator/ │ │ │ │ └── springmvc/ │ │ │ │ ├── ClassMethodNoHttpMethod.java │ │ │ │ ├── ClassMethodNoPath.java │ │ │ │ ├── ClassMultiHttpMethod.java │ │ │ │ ├── ClassMultiPath.java │ │ │ │ ├── Echo.java │ │ │ │ ├── MethodDefaultParameter.java │ │ │ │ ├── MethodEmptyPath.java │ │ │ │ ├── MethodMixupAnnotations.java │ │ │ │ ├── MethodMultiPath.java │ │ │ │ ├── MethodResponseEntity.java │ │ │ │ ├── MultiDefaultPath.java │ │ │ │ ├── TestSpringmvc.java │ │ │ │ ├── TestTwoSameNameModels.java │ │ │ │ ├── model/ │ │ │ │ │ ├── DefaultParameterSchema.java │ │ │ │ │ ├── Generic.java │ │ │ │ │ ├── RestControllerWithPathSchema.java │ │ │ │ │ ├── SwaggerTestTarget.java │ │ │ │ │ ├── SwaggerTestTarget_ValueOverWritePath.java │ │ │ │ │ ├── TestParam.java │ │ │ │ │ ├── TestProducer.java │ │ │ │ │ ├── same1/ │ │ │ │ │ │ ├── SameModel.java │ │ │ │ │ │ └── SameModelThrow.java │ │ │ │ │ └── same2/ │ │ │ │ │ ├── SameModel.java │ │ │ │ │ └── SameModelThrow.java │ │ │ │ ├── processor/ │ │ │ │ │ └── annotation/ │ │ │ │ │ └── RequestPartAnnotationProcessorTest.java │ │ │ │ └── property/ │ │ │ │ └── creator/ │ │ │ │ └── MultipartFilePropertyCreatorTest.java │ │ │ └── resources/ │ │ │ └── schemas/ │ │ │ ├── DefaultParameterSchema.yaml │ │ │ ├── DemoRest.yaml │ │ │ ├── MethodEmptyPath.yaml │ │ │ ├── RestControllerWithPathSchema.yaml │ │ │ ├── asyncResponseEntity.yaml │ │ │ ├── cookie.yaml │ │ │ ├── defaultParameter.yaml │ │ │ ├── echo.yaml │ │ │ ├── emptyPath.yaml │ │ │ ├── enumBody.yaml │ │ │ ├── inheritHttpMethod.yaml │ │ │ ├── mixupAnnotations.yaml │ │ │ ├── nestedListString.yaml │ │ │ ├── part.yaml │ │ │ ├── partAnnotation.yaml │ │ │ ├── partArray.yaml │ │ │ ├── partArrayAnnotation.yaml │ │ │ ├── partList.yaml │ │ │ ├── partListAnnotation.yaml │ │ │ ├── rawJsonStringMethod.yaml │ │ │ ├── requestMappingHttpMethod.yaml │ │ │ ├── responseEntity.yaml │ │ │ ├── swaggerTestTarget.yaml │ │ │ ├── swaggerTestTarget_ValueOverWritePath.yaml │ │ │ ├── testBlankMediaType.yaml │ │ │ ├── testCompletableFutureResponseEntityOptional.yaml │ │ │ ├── testMultipleMediaType.yaml │ │ │ ├── testObjectParam.yaml │ │ │ ├── testResponseEntityOptional.yaml │ │ │ ├── testSimpleParam.yaml │ │ │ └── testSingleMediaType.yaml │ │ └── pom.xml │ └── swagger-invocation/ │ ├── invocation-core/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ ├── engine/ │ │ │ │ │ ├── SwaggerConsumer.java │ │ │ │ │ ├── SwaggerConsumerOperation.java │ │ │ │ │ ├── SwaggerEnvironment.java │ │ │ │ │ ├── SwaggerProducer.java │ │ │ │ │ └── SwaggerProducerOperation.java │ │ │ │ └── invocation/ │ │ │ │ ├── AsyncResponse.java │ │ │ │ ├── InvocationType.java │ │ │ │ ├── Response.java │ │ │ │ ├── SwaggerInvocation.java │ │ │ │ ├── arguments/ │ │ │ │ │ ├── AbstractArgumentsMapperCreator.java │ │ │ │ │ ├── ArgumentMapper.java │ │ │ │ │ ├── ArgumentsMapper.java │ │ │ │ │ ├── ContextArgumentMapperFactory.java │ │ │ │ │ ├── consumer/ │ │ │ │ │ │ ├── ArgumentsMapperCommon.java │ │ │ │ │ │ ├── ArgumentsMapperDirectReuse.java │ │ │ │ │ │ ├── ConsumerArgumentMapper.java │ │ │ │ │ │ ├── ConsumerArgumentSame.java │ │ │ │ │ │ ├── ConsumerArgumentToBodyField.java │ │ │ │ │ │ ├── ConsumerArgumentsMapperCreator.java │ │ │ │ │ │ ├── ConsumerBeanParamMapper.java │ │ │ │ │ │ ├── ConsumerContextArgumentMapperFactory.java │ │ │ │ │ │ ├── ConsumerInvocationContextMapper.java │ │ │ │ │ │ └── ConsumerInvocationContextMapperFactory.java │ │ │ │ │ └── producer/ │ │ │ │ │ ├── AbstractProducerContextArgMapper.java │ │ │ │ │ ├── ProducerArgumentMapper.java │ │ │ │ │ ├── ProducerArgumentSame.java │ │ │ │ │ ├── ProducerArgumentsMapper.java │ │ │ │ │ ├── ProducerArgumentsMapperCreator.java │ │ │ │ │ ├── ProducerBeanParamMapper.java │ │ │ │ │ ├── ProducerContextArgumentMapperFactory.java │ │ │ │ │ ├── ProducerInvocationContextMapper.java │ │ │ │ │ ├── ProducerInvocationContextMapperFactory.java │ │ │ │ │ └── SwaggerBodyFieldToProducerArgument.java │ │ │ │ ├── context/ │ │ │ │ │ ├── ContextUtils.java │ │ │ │ │ ├── HttpStatus.java │ │ │ │ │ ├── HttpStatusManager.java │ │ │ │ │ ├── InvocationContext.java │ │ │ │ │ ├── InvocationContextCompletableFuture.java │ │ │ │ │ ├── TransportContext.java │ │ │ │ │ └── VertxTransportContext.java │ │ │ │ ├── converter/ │ │ │ │ │ ├── Converter.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── part/ │ │ │ │ │ ├── BytesToPartConverter.java │ │ │ │ │ ├── FileToPartConverter.java │ │ │ │ │ ├── InputStreamToPartConverter.java │ │ │ │ │ ├── PartListToPartArrayConverter.java │ │ │ │ │ ├── PartListToPartListConverter.java │ │ │ │ │ ├── PartToPartConverter.java │ │ │ │ │ └── ResourceToPartConverter.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── CommonExceptionData.java │ │ │ │ │ ├── ExceptionFactory.java │ │ │ │ │ └── InvocationException.java │ │ │ │ ├── generator/ │ │ │ │ │ ├── InvocationContextProcessor.java │ │ │ │ │ └── ScbResponseProcessor.java │ │ │ │ └── response/ │ │ │ │ ├── ResponseMapperFactories.java │ │ │ │ ├── ResponseMapperFactory.java │ │ │ │ ├── ResponseMetaMapper.java │ │ │ │ ├── ResponsesMeta.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── CompletableFutureConsumerResponseMapperFactory.java │ │ │ │ │ ├── ConsumerResponseMapper.java │ │ │ │ │ ├── ConsumerResponseMapperFactory.java │ │ │ │ │ ├── CseResponseConsumerResponseMapperFactory.java │ │ │ │ │ ├── DefaultConsumerResponseMapper.java │ │ │ │ │ ├── DefaultConsumerResponseMapperFactory.java │ │ │ │ │ ├── OptionalConsumerResponseMapper.java │ │ │ │ │ └── OptionalConsumerResponseMapperFactory.java │ │ │ │ └── producer/ │ │ │ │ ├── CompletableFutureProducerResponseMapperFactory.java │ │ │ │ ├── CseResponseProducerResponseMapperFactory.java │ │ │ │ ├── DefaultProducerResponseMapper.java │ │ │ │ ├── DefaultProducerResponseMapperFactory.java │ │ │ │ ├── OptionalProducerResponseMapper.java │ │ │ │ ├── OptionalProducerResponseMapperFactory.java │ │ │ │ ├── ProducerResponseMapper.java │ │ │ │ └── ProducerResponseMapperFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ ├── org.apache.servicecomb.swagger.generator.ResponseTypeProcessor │ │ │ ├── org.apache.servicecomb.swagger.generator.SwaggerContextRegister │ │ │ ├── org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory │ │ │ ├── org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory │ │ │ ├── org.apache.servicecomb.swagger.invocation.converter.Converter │ │ │ ├── org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory │ │ │ └── org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ ├── core/ │ │ │ ├── TestException.java │ │ │ └── TestResponse.java │ │ └── swagger/ │ │ ├── engine/ │ │ │ ├── TestSwaggerEnvironment.java │ │ │ └── TestSwaggerProducerOperation.java │ │ └── invocation/ │ │ ├── TestSwaggerInvocation.java │ │ ├── arguments/ │ │ │ ├── consumer/ │ │ │ │ ├── TestJaxrsV1V1.java │ │ │ │ ├── TestJaxrsV1V2.java │ │ │ │ ├── TestJaxrsV2V1.java │ │ │ │ ├── TestJaxrsV2V2.java │ │ │ │ ├── TestPojoOneArg.java │ │ │ │ ├── TestPojoV1V1.java │ │ │ │ ├── TestPojoV1V2.java │ │ │ │ ├── TestPojoV2V1.java │ │ │ │ ├── TestPojoV2V2.java │ │ │ │ ├── TestSpringmvcV1V1.java │ │ │ │ ├── TestSpringmvcV1V2.java │ │ │ │ ├── TestSpringmvcV2V1.java │ │ │ │ └── TestSpringmvcV2V2.java │ │ │ └── producer/ │ │ │ ├── TestJaxrs.java │ │ │ ├── TestPojo.java │ │ │ ├── TestPojoOneArg.java │ │ │ └── TestSpringmvc.java │ │ ├── context/ │ │ │ ├── TestContextUtils.java │ │ │ └── TestInvocationContext.java │ │ ├── converter/ │ │ │ └── impl/ │ │ │ └── part/ │ │ │ ├── PartListToPartArrayConverterTest.java │ │ │ ├── PartListToPartListConverterTest.java │ │ │ ├── PartToPartConverterTest.java │ │ │ ├── TestBytesToPartConverter.java │ │ │ ├── TestFileToPartConverter.java │ │ │ ├── TestInputStreamToPartConverter.java │ │ │ └── TestResourceToPartConverter.java │ │ ├── exception/ │ │ │ └── CommonExceptionDataTest.java │ │ ├── models/ │ │ │ ├── JaxrsImpl.java │ │ │ ├── Person.java │ │ │ ├── PojoConsumerIntf.java │ │ │ ├── PojoImpl.java │ │ │ ├── ProducerImpl.java │ │ │ └── SpringmvcImpl.java │ │ ├── response/ │ │ │ ├── TestResponsesMeta.java │ │ │ ├── consumer/ │ │ │ │ └── TestConsumerResponseMapperFactories.java │ │ │ └── producer/ │ │ │ └── TestProducerResponseMapperFactories.java │ │ └── schemas/ │ │ ├── ConsumerAddBodyV1.java │ │ ├── ConsumerAddBodyV2.java │ │ ├── ConsumerAddV1.java │ │ ├── ConsumerAddV2.java │ │ ├── ConsumerAddWithContext.java │ │ ├── ConsumerOneArg.java │ │ ├── JaxrsAddBeanParamV1.java │ │ ├── JaxrsAddBeanParamV2.java │ │ ├── JaxrsAddBodyV1.java │ │ ├── JaxrsAddBodyV2.java │ │ ├── JaxrsAddV1.java │ │ ├── JaxrsAddV2.java │ │ ├── PojoAddBodyV1.java │ │ ├── PojoAddBodyV2.java │ │ ├── PojoAddV1.java │ │ ├── PojoAddV2.java │ │ ├── PojoAddWithContextV1.java │ │ ├── PojoOneArg.java │ │ ├── SpringmvcAddBodyV1.java │ │ ├── SpringmvcAddBodyV2.java │ │ ├── SpringmvcAddV1.java │ │ ├── SpringmvcAddV2.java │ │ ├── SpringmvcAddWrapperV1.java │ │ ├── SpringmvcAddWrapperV2.java │ │ └── models/ │ │ ├── AddBeanParamV1.java │ │ ├── AddBeanParamV2.java │ │ ├── AddWrapperV1.java │ │ └── AddWrapperV2.java │ ├── invocation-jaxrs/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ └── invocation/ │ │ │ │ └── jaxrs/ │ │ │ │ └── response/ │ │ │ │ ├── JaxrsConsumerResponseMapper.java │ │ │ │ ├── JaxrsConsumerResponseMapperFactory.java │ │ │ │ ├── JaxrsProducerResponseMapper.java │ │ │ │ └── JaxrsProducerResponseMapperFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ ├── org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory │ │ │ └── org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── swagger/ │ │ │ └── invocation/ │ │ │ └── jaxrs/ │ │ │ └── response/ │ │ │ ├── TestJaxrsConsumerResponseMapper.java │ │ │ ├── TestJaxrsProducerResponseMapper.java │ │ │ └── TestJaxrsProducerResponseMapperFactory.java │ │ └── resources/ │ │ └── log4j2.xml │ ├── invocation-springmvc/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── apache/ │ │ │ │ └── servicecomb/ │ │ │ │ └── swagger/ │ │ │ │ └── invocation/ │ │ │ │ ├── converter/ │ │ │ │ │ ├── PartListToMultipartArrayConverter.java │ │ │ │ │ ├── PartListToMultipartListConverter.java │ │ │ │ │ ├── PartToMultipartConverter.java │ │ │ │ │ └── PartToMultipartFile.java │ │ │ │ └── springmvc/ │ │ │ │ └── response/ │ │ │ │ ├── SpringmvcConsumerResponseMapper.java │ │ │ │ ├── SpringmvcConsumerResponseMapperFactory.java │ │ │ │ ├── SpringmvcProducerResponseMapper.java │ │ │ │ └── SpringmvcProducerResponseMapperFactory.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ ├── org.apache.servicecomb.swagger.invocation.converter.Converter │ │ │ ├── org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory │ │ │ └── org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── swagger/ │ │ │ └── invocation/ │ │ │ ├── converter/ │ │ │ │ └── TestPartToMultipartFile.java │ │ │ └── springmvc/ │ │ │ └── response/ │ │ │ ├── TestSpringmvcConsumerResponseMapper.java │ │ │ ├── TestSpringmvcProducerResponseMapper.java │ │ │ └── TestSpringmvcProducerResponseMapperFactory.java │ │ └── resources/ │ │ └── log4j2.xml │ ├── invocation-validator/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── swagger/ │ │ │ └── invocation/ │ │ │ └── validator/ │ │ │ └── DefaultParameterNameProvider.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── swagger/ │ │ └── invocation/ │ │ └── validator/ │ │ └── TestDefaultParameterNameProvider.java │ └── pom.xml ├── tracing/ │ ├── README.md │ ├── pom.xml │ ├── tracing-common/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── tracing/ │ │ └── Span.java │ └── tracing-zipkin/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── tracing/ │ │ └── zipkin/ │ │ ├── EnableZipkinTracing.java │ │ ├── ZipkinSpanAspect.java │ │ ├── ZipkinSpanAspectConfig.java │ │ └── ZipkinTracingAdviser.java │ └── test/ │ └── java/ │ └── org/ │ └── apache/ │ └── servicecomb/ │ └── tracing/ │ └── zipkin/ │ ├── ZipkinSpanAspectTest.java │ ├── ZipkinTracingAdviserTest.java │ └── app/ │ └── ZipkinSpanTestApplication.java └── transports/ ├── pom.xml ├── transport-common/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── transport/ │ │ └── common/ │ │ └── TransportConfigUtils.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── transport/ │ │ └── common/ │ │ └── TestTransportConfigUtils.java │ └── resources/ │ └── log4j2.xml ├── transport-highway/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── transport/ │ │ │ └── highway/ │ │ │ ├── HighwayClient.java │ │ │ ├── HighwayClientConnection.java │ │ │ ├── HighwayClientConnectionPool.java │ │ │ ├── HighwayClientFilter.java │ │ │ ├── HighwayClientPackage.java │ │ │ ├── HighwayClientPoolFactory.java │ │ │ ├── HighwayCodec.java │ │ │ ├── HighwayConfig.java │ │ │ ├── HighwayOutputStream.java │ │ │ ├── HighwayProducerInvocationFlow.java │ │ │ ├── HighwayServer.java │ │ │ ├── HighwayServerCodecFilter.java │ │ │ ├── HighwayServerConnection.java │ │ │ ├── HighwayServerVerticle.java │ │ │ ├── HighwayTransport.java │ │ │ ├── HighwayTransportContext.java │ │ │ ├── MsgType.java │ │ │ ├── TransportHighwayConfiguration.java │ │ │ └── message/ │ │ │ ├── Headers.java │ │ │ ├── LoginRequest.java │ │ │ ├── LoginResponse.java │ │ │ ├── RequestHeader.java │ │ │ └── ResponseHeader.java │ │ └── resources/ │ │ ├── LoginRequest.proto │ │ ├── LoginResponse.proto │ │ ├── META-INF/ │ │ │ ├── services/ │ │ │ │ └── org.apache.servicecomb.core.Transport │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ ├── RequestHeader.proto │ │ ├── ResponseHeader.proto │ │ └── microservice.yaml │ └── test/ │ └── java/ │ └── org/ │ └── apache/ │ └── servicecomb/ │ └── transport/ │ ├── common/ │ │ └── MockUtil.java │ └── highway/ │ ├── HighwayServerCodecFilterTest.java │ ├── TestHighwayClient.java │ ├── TestHighwayTransport.java │ ├── TestHighwayVerticle.java │ └── message/ │ ├── TestHeaders.java │ ├── TestLoginRequest.java │ ├── TestLoginResponse.java │ ├── TestRequestHeader.java │ └── TestResponseHeader.java └── transport-rest/ ├── pom.xml ├── transport-rest-client/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── transport/ │ │ │ └── rest/ │ │ │ └── client/ │ │ │ ├── BoundaryFactory.java │ │ │ ├── Http2TransportHttpClientOptionsSPI.java │ │ │ ├── HttpClientRequestFactory.java │ │ │ ├── HttpTransportHttpClientOptionsSPI.java │ │ │ ├── RestClientCodecFilter.java │ │ │ ├── RestClientDecoder.java │ │ │ ├── RestClientEncoder.java │ │ │ ├── RestClientExceptionCodes.java │ │ │ ├── RestClientRequestParameters.java │ │ │ ├── RestClientRequestParametersImpl.java │ │ │ ├── RestClientSender.java │ │ │ ├── RestClientSenderFilter.java │ │ │ ├── RestClientTransportContext.java │ │ │ ├── RestClientTransportContextFactory.java │ │ │ ├── TransportClientConfig.java │ │ │ ├── TransportRestClientConfiguration.java │ │ │ ├── WebSocketClientCodecFilter.java │ │ │ └── WebSocketClientTransportContext.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── services/ │ │ │ └── org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── transport/ │ │ └── rest/ │ │ └── client/ │ │ ├── FakeRestTransport.java │ │ └── RestFeatureController.java │ └── resources/ │ └── log4j2.xml ├── transport-rest-servlet/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── apache/ │ │ │ └── servicecomb/ │ │ │ └── transport/ │ │ │ └── rest/ │ │ │ └── servlet/ │ │ │ ├── RestAsyncListener.java │ │ │ ├── RestServlet.java │ │ │ ├── RestServletInjector.java │ │ │ ├── RestServletProducerInvocationCreator.java │ │ │ ├── ServletConfig.java │ │ │ ├── ServletRestDispatcher.java │ │ │ ├── ServletRestTransport.java │ │ │ └── ServletUtils.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.apache.servicecomb.core.Transport │ └── test/ │ └── java/ │ └── org/ │ └── apache/ │ └── servicecomb/ │ └── transport/ │ └── rest/ │ └── servlet/ │ ├── TestRestAsyncListener.java │ ├── TestRestServlet.java │ ├── TestRestServletInjector.java │ ├── TestServletRestTransport.java │ └── TestServletUtils.java └── transport-rest-vertx/ ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── org/ │ │ └── apache/ │ │ └── servicecomb/ │ │ └── transport/ │ │ └── rest/ │ │ └── vertx/ │ │ ├── AbstractVertxHttpDispatcher.java │ │ ├── GlobalRestFailureHandler.java │ │ ├── HttpServerExceptionHandler.java │ │ ├── RestBodyHandler.java │ │ ├── RestServerVerticle.java │ │ ├── TransportConfig.java │ │ ├── VertxHttpDispatcher.java │ │ ├── VertxRestDispatcher.java │ │ ├── VertxRestTransport.java │ │ ├── WebSocketDispatcher.java │ │ ├── WebSocketProducerInvocationFlow.java │ │ └── WebSocketTransport.java │ └── resources/ │ └── META-INF/ │ └── services/ │ ├── org.apache.servicecomb.core.Transport │ └── org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher └── test/ └── java/ └── org/ └── apache/ └── servicecomb/ └── transport/ └── rest/ └── vertx/ ├── MockForRestServerVerticle.java ├── MockHttpServerResponse.java ├── TestAbstractVertxHttpDispatcher.java ├── TestRestServerVerticle.java ├── TestVertxRestDispatcher.java └── TestVertxRestTransport.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug Report description: Create a report to help us improve title: "[BUG] - Issue Title" labels: bug body: - type: markdown attributes: value: "## Describe the bug\nA clear and concise description of what the bug is." - type: textarea attributes: label: Steps to Reproduce description: "List the steps to reproduce the bug." placeholder: "1. Go to '...'\n2. Click on '...'\n3. See error" - type: textarea attributes: label: Expected Behavior description: "Describe the behavior you expected to see." placeholder: "A clear and concise description of what you expected to happen." - type: input attributes: label: Servicecomb Version description: "Please provide your Servicecomb version" placeholder: "3.2.4" - type: textarea attributes: label: Additional Context description: "Add any other context about the problem here." placeholder: "Any additional information that might help us debug the issue." ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: "Contact Support" url: "mailto:dev@servicecomb.apache.org" about: "If you need help, please contact us." templates: - name: "Bug Report" description: "Report a bug or issue." filename: "bug_report.yml" - name: "Feature Request" description: "Request a new feature or improvement." filename: "feature_request.yml" - name: "Documentation Issue" description: "Report an issue with documentation." filename: "documentation_issue.yml" - name: "Question" description: "Ask a question or seek help." filename: "question.yml" ================================================ FILE: .github/ISSUE_TEMPLATE/documentation_issue.yml ================================================ name: Documentation Issue description: Report an issue with the documentation title: "[DOCUMENTATION] - Documentation Issue Title" labels: documentation body: - type: markdown attributes: value: "## Describe the documentation issue\nA clear and concise description of the issue with the documentation." - type: textarea attributes: label: Location in documentation description: "Please provide the URL or section where the issue appears." placeholder: "e.g. https://docs.example.com/section" - type: textarea attributes: label: What should be changed? description: "Please describe what changes or improvements should be made to the documentation." placeholder: "Explain how we can improve the content." - type: textarea attributes: label: Additional Context description: "Provide any other context or examples here." placeholder: "Additional comments or suggestions for the documentation." ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature Request description: Suggest an idea or feature for this project title: "[FEATURE] - Feature Title" labels: enhancement body: - type: markdown attributes: value: "## Describe the feature request\nA clear and concise description of what you want to be added to the project." - type: textarea attributes: label: Problem this feature would solve description: "Please describe the problem or challenge you're facing." placeholder: "Explain why this feature would be helpful." - type: textarea attributes: label: Describe the solution description: "How do you envision this feature working?" placeholder: "Provide a description of how the feature would solve the problem." - type: textarea attributes: label: Alternatives considered description: "Have you considered other solutions or alternatives? If yes, describe them." placeholder: "Provide any alternative solutions you've thought about." - type: textarea attributes: label: Additional Context description: "Any other context or information that might help us implement this feature." placeholder: "Include any links, mockups, or additional details." ================================================ FILE: .github/ISSUE_TEMPLATE/question.yml ================================================ name: General Question description: Ask a general question related to the project title: "[QUESTION] - Question Title" labels: question body: - type: markdown attributes: value: "## What is your question?\nDescribe the question or issue you are facing. Be as specific as possible." - type: textarea attributes: label: Steps to reproduce (if applicable) description: "If your question is about a specific problem or bug, please include steps to reproduce it." placeholder: "Provide steps, if applicable." - type: textarea attributes: label: What have you tried so far? description: "Please explain what you have already tried to resolve the issue or answer the question." placeholder: "Describe the steps you've taken." - type: textarea attributes: label: Additional context description: "Provide any other context or information related to the question." placeholder: "Include any relevant links or background information." ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ Follow this checklist to help us incorporate your contribution quickly and easily: - [ ] Make sure there is a [JIRA issue](https://issues.apache.org/jira/browse/SCB) filed for the change (usually before you start working on it). Trivial changes like typos do not require a JIRA issue. Your pull request should address just this issue, without pulling in other changes. - [ ] Each commit in the pull request should have a meaningful subject line and body. - [ ] Format the pull request title like `[SCB-XXX] Fixes bug in ApproximateQuantiles`, where you replace `SCB-XXX` with the appropriate JIRA issue. - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. - [ ] Run `mvn clean install -Pit` to make sure basic checks pass. A more thorough check will be performed on your pull request automatically. - [ ] If this contribution is large, please file an Apache [Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf). --- ================================================ FILE: .github/dependabot.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" - package-ecosystem: "maven" directory: "/" schedule: interval: "monthly" open-pull-requests-limit: 20 ================================================ FILE: .github/workflows/checkstyle.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # name: checkstyle on: pull_request: branches: - master jobs: checkstyle: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up jdk uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - name: checkstyle run: mvn checkstyle:check -B -Pit ================================================ FILE: .github/workflows/linelint.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # name: line lint on: pull_request: branches: - master jobs: linelint: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: install linelint run: cargo install linelint-cli - name: Run linelint check run: linelint check ================================================ FILE: .github/workflows/maven.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # name: Java CI with Maven on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up jdk uses: actions/setup-java@v5 with: java-version: '17.0.8' distribution: 'temurin' - name: Set up Maven uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.9 - uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Compilation and Installation run: mvn clean verify -Dcheckstyle.skip=true -B -Pdocker -Pjacoco -Pit -Pcoverage ================================================ FILE: .github/workflows/rat_check.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # name: rat check on: push: branches: - master pull_request: branches: - master jobs: rat_check: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up jdk uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - name: rat check run: mvn apache-rat:check -B -Pit,release ================================================ FILE: .github/workflows/typo_check.yml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # name: typo check on: pull_request: branches: - master jobs: typo-check: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 # To run the typo check locally, you can follow these steps: # 1. Install typos locally using cargo: # cargo install typos-cli # 2. Run the typo check with the following command: # typos - name: Check typos uses: crate-ci/typos@v1.38.1 ================================================ FILE: .gitignore ================================================ # Output Directory target/ # C pre-compile *.gch *.pch # C compile *.a *.o *.ko *.la *.lo *.obj *.elf *.so *.so.* *.dylib *.exe *.lib *.dll *.out *.app *.hex # Debug files *.dSYM/ # Java *.class # Java Package Files *.jar *.war *.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # Zip Files *.rar *.zip *.7z *.tar *.gz # Ant build/ # Compiled Python __pycache__/ *.py[cod] *py.class # Eclipse .settings/ .classpath .project # IntelliJ, based on http://devnet.jetbrains.net/docs/DOC-1186 .idea/ *.iml *.ipr *.iws # logs and trace *.log *.trace *.dat # vi swap *.swp # Backup Files *.bak *.old # SVN metadata .svn/ # Mac .DS_Store # gradle .gradle ================================================ FILE: .typos.toml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # [default.extend-words] "fo" = "fo" "VERTX" = "VERTX" "Vertx" = "Vertx" "vertx" = "vertx" [files] extend-exclude = [ "**/cobertura.ser" ] [default] extend-ignore-words-re = [ "Verticle", "verticle", "VERTICLE", "Prelease", "cobertura", "ser", ] ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ======================================================================= Apache ServiceComb Java Chassis Subcomponents: The Apache ServiceComb Java Chassis project contains subcomponents with separate copyright notices and license terms. Your use of the source for these subcomponents is subject to the terms and conditions of the following licenses. ================================================================ For foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java ================================================================ This product bundles files from vertx which is licensed under the Apache License v2. For details, see https://github.com/vert-x3/vertx-web ================================================================ For swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java ================================================================ This product bundles files from swagger which is licensed under the Apache License v2. For details, see https://github.com/swagger-api/swagger-core ================================================================ For foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java ================================================================ This product bundles files from protostuff which is licensed under the Apache License v2. For details, see https://github.com/protostuff/protostuff ================================================================ For foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto For foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto ================================================================ This product bundles files from swagger which is licensed under the BSD-3-Clause. For details, see https://github.com/protocolbuffers/protobuf ================================================ FILE: NOTICE ================================================ Apache ServiceComb Java Chassis Copyright 2017-2025 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ================================================ FILE: README.md ================================================ # Java Chassis [中文](README_ZH.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) Apache ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features. > If you find this project helpful, please don't forget to `star` it. # releases | Release Train | Latest Version | Compiled JDK Version | Tested JDK Version | Open API | Notes | |----------------|----------------|----------------------|--------------------|----------|--------------------------| | Java Chassis 3 | 3.3.0 | OpenJDK 17 | OpenJDK 17 | 3.0.x | Depends on Spring Boot 3 | | Java Chassis 2 | 2.8.24 | OpenJDK 8 | OpenJDK 8, 11, 17 | 2.0.x | Depends on Spring 5 | | Java Chassis 1 | 1.3.11 | OpenJDK 8 | OpenJDK 8 | 2.0.x | End of Support | Java Chassis core dependencies | Java Chassis | Spring Boot | Vert.x | Swagger | Jackson | |--------------|-------------|--------|---------|---------| | 3.3.x | 3.4.x | 4.5.x | 2.2.x | 2.18.x | | 3.2.x | 3.3.x | 4.5.x | 2.2.x | 2.18.x | > NOTICE: Since Open API 3.0.x is not compatible with 2.0.x, Java Chassis 2 and Java Chassis 1 can not > work together with Java Chassis 3. All related consumers, providers and edge service need use Java Chassis 3 when upgrading. > NOTICE: Java Chassis 1 reached its end of support now after it's first release from 2018. # Quick Start * Define API ```java @RequestMapping(path = "/provider") public interface ProviderService { @GetMapping("/sayHello") String sayHello(@RequestParam("name") String name); } ``` * Provider service ```java @RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) public class ProviderController implements ProviderService { @Override public String sayHello(String name) { return "Hello " + name; } } ``` * Consumer service ```java @Configuration public class ProviderServiceConfiguration { @Bean public ProviderService providerService() { return Invoker.createProxy("provider", "ProviderController", ProviderService.class); } } ``` Invoke Provider service with RPC ```java @RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) public class ConsumerController implements ConsumerService { private ProviderService providerService; @Autowired public void setProviderService(ProviderService providerService) { this.providerService = providerService; } @Override public String sayHello(String name) { return providerService.sayHello(name); } } ``` Try out this example [here](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/first-sample.html) . # Documentation Project documentation is available on the [ServiceComb Java Chassis Developer Guide][java-chassis-developer-guide]. [java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ # Building You don’t need to build from source to use Java Chassis (binaries in apache nexus ), but if you want to try out the latest and greatest, Java Chassis can be easily built with the maven. You also need JDK 17. mvn clean install The first build may take a longer than expected as Maven downloads all the dependencies. # Automated Testing To build the docker image and run the integration tests with docker, you can use maven docker profile mvn clean install -Pdocker -Pit If you are using docker machine, please use the following command mvn clean install -Pdocker -Pit -Pdocker-machine # Contact Bugs: [issues](https://issues.apache.org/jira/browse/SCB) mailing list: [subscribe](mailto:dev-subscribe@servicecomb.apache.org) [dev](https://lists.apache.org/list.html?dev@servicecomb.apache.org) # Contributing See [CONTRIBUTING](http://servicecomb.apache.org/developers/contributing) for details on submitting patches and the contribution workflow. # License Licensed under an [Apache 2.0 license](LICENSE). ================================================ FILE: README_ZH.md ================================================ # Java Chassis | [English](README.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) Apache ServiceComb Java Chassis 给开发者提供一个快速构建微服务的JAVA SDK。它包含如下特性: * 基于Open API的契约优先(API First)开发模式,满足开发过程可管理、开发规范可验证要求。 * 多种开发风格,REST(JAX-RS、Spring MVC)和RPC等,高效支持遗留系统迁移和新系统开发场景。 * 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等,满足不同场景对于性能、韧性的需求。 * 统一一致的服务提供者、服务消费者处理链,易于扩展新功能。 * 提供服务发现、配置管理、熔断、限流、灰度发布等开箱即用的服务治理能力。 可以通过[设计选型参考](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/design.html) 了解更多特性和设计原理。 > 如果发现项目能帮助到您,别忘了点击右上角`star`表示鼓励。 # 发布版本 | 版本火车 | 最新版本 | 编译的JDK版本 | 支持的JDK版本 | Open API | 备注 | |----------------|--------|------------|-------------------|----------|-----------------| | Java Chassis 3 | 3.3.0 | OpenJDK 17 | OpenJDK 17 | 3.0.x | 依赖Spring Boot 3 | | Java Chassis 2 | 2.8.24 | OpenJDK 8 | OpenJDK 8, 11, 17 | 2.0.x | 依赖Spring 5 | | Java Chassis 1 | 1.3.11 | OpenJDK 8 | OpenJDK 8 | 2.0.x | 停止更新 | Java Chassis 的核心依赖 | Java Chassis | Spring Boot | Vert.x | Swagger | Jackson | |--------------|-------------|--------|---------|---------| | 3.3.x | 3.4.x | 4.5.x | 2.2.x | 2.18.x | | 3.2.x | 3.3.x | 4.5.x | 2.2.x | 2.18.x | > NOTICE: Open API 3.0.x 不兼容 2.0.x, 因此Java Chassis 2、Java Chassis 1不能与Java Chassis 3共存互访. 升级Java Chassis 3, 需要将相关的消费者、提供者和边缘服务同时升级. > NOTICE: Java Chassis 1 第一个版本于2018发布,已经停止更新. # 快速开始 * 定义服务契约 ```java @RequestMapping(path = "/provider") public interface ProviderService { @GetMapping("/sayHello") String sayHello(@RequestParam("name") String name); } ``` * 定义提供者 ```java @RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) public class ProviderController implements ProviderService { @Override public String sayHello(String name) { return "Hello " + name; } } ``` * 定义消费者 ```java @Configuration public class ProviderServiceConfiguration { @Bean public ProviderService providerService() { return Invoker.createProxy("provider", "ProviderController", ProviderService.class); } } ``` 使用RPC方式访问提供者。 ```java @RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) public class ConsumerController implements ConsumerService { private ProviderService providerService; @Autowired public void setProviderService(ProviderService providerService) { this.providerService = providerService; } @Override public String sayHello(String name) { return providerService.sayHello(name); } } ``` 下载并体验上述[示例项目](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/first-sample.html) . # 用户文档 请访问 [ServiceComb Java Chassis 开发指南][java-chassis-developer-guide]. [java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ # 编译 Java Chassis 开发者可以通过MAVEN仓库使用Java Chassis。 如果需要构建项目,需要使用JDK 17版本,并预先安装maven。 mvn clean install # 运行测试用例 开发者需要预先安装docker。 mvn clean install -Pdocker -Pit 使用docker machine。 mvn clean install -Pdocker -Pit -Pdocker-machine # 联系我们 报告缺陷: [issues](https://issues.apache.org/jira/browse/SCB) 邮件列表: [subscribe](mailto:dev-subscribe@servicecomb.apache.org) [dev](https://lists.apache.org/list.html?dev@servicecomb.apache.org) # 参与代码提交 参考 [如何做贡献](http://servicecomb.apache.org/cn/developers/contributing). # License Licensed under an [Apache 2.0 license](LICENSE). ================================================ FILE: ci/README.md ================================================ # Java Chassis Code Checks * Compilation and Installation see .github/workflows/maven.yml * Checkstyle see .github/workflows/checkstyle.yml * Rat Check see .github/workflows/rat_check.yml * Spot Bugs see .github/workflows/spotbugs.yml * OWASP Dependency Check `mvn verify -Powasp-dependency-check` . Very Slow, run manually. * Distribution `mvn clean deploy -Dcheckstyle.skip -Dspotbugs.skip=true -Dmaven.javadoc.skip=true -DskipTests -Prelease -Pdistribution` . Run manually when preparing a release. ================================================ FILE: ci/checkstyle/checkstyle.xml ================================================ ================================================ FILE: ci/checkstyle/suppressions.xml ================================================ ================================================ FILE: ci/spotbugs/exclude.xml ================================================ ================================================ FILE: clients/README.md ================================================ # About This module implements common http clients for servicecomb-service-center, servicecomb-kie and other 3rd-party services. This module is independent on servicecomb-java-chassis, and can be used in many other projects like Spring Cloud, Dubbo, etc. # 关于 这个模块给 servicecomb-service-center, servicecomb-kie 以及其他第三方服务实现通用的 Http Client。 这个模块独立于 servicecomb-java-chassis, 可以用于 Spring Cloud, Dubbo 等项目。 ================================================ FILE: clients/config-center-client/pom.xml ================================================ clients org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-center-client ServiceComb::Clients::Config Center Client org.apache.servicecomb http-client-common org.apache.servicecomb config-clients-common ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client; import java.util.List; import org.apache.servicecomb.http.client.common.AbstractAddressManager; import org.apache.servicecomb.http.client.common.URLEndPoint; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ConfigCenterAddressManager extends AbstractAddressManager { public ConfigCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, String availableZone) { super(projectName, addresses, region, availableZone); eventBus.register(this); } @Override protected String normalizeUri(String endpoint) { String address = new URLEndPoint(endpoint).toString(); return formatAddress(address); } @Subscribe public void onRefreshEndpointEvent(RefreshEndpointEvent event) { refreshEndpoint(event, RefreshEndpointEvent.CONFIG_CENTER_NAME); } } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.apache.servicecomb.config.common.exception.OperationException; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.eventbus.EventBus; public class ConfigCenterClient implements ConfigCenterOperation { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterClient.class); public static final String DEFAULT_APP_SEPARATOR = "@"; public static final String DEFAULT_SERVICE_SEPARATOR = "#"; public static final String REVISION = "revision"; public static final String APPLICATION_CONFIG = "application"; public static final String DARK_LAUNCH = "darklaunch@"; private static final String ADDRESS_CHECK_PATH = "/v3/default/configuration/health?mode=readiness"; private final HttpTransport httpTransport; private final ConfigCenterAddressManager addressManager; private final Map> dimensionConfigNames = new HashMap<>(); private EventBus eventBus; public ConfigCenterClient(ConfigCenterAddressManager addressManager, HttpTransport httpTransport) { this.addressManager = addressManager; this.httpTransport = httpTransport; } public void setEventBus(EventBus eventBus) { this.eventBus = eventBus; addressManager.setEventBus(eventBus); } @Override public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request, String address) { String dimensionsInfo = buildDimensionsInfo(request, true); QueryConfigurationsResponse queryConfigurationsResponse = new QueryConfigurationsResponse(); Map configurations = new HashMap<>(); String uri = null; try { uri = address + "/configuration/items?dimensionsInfo=" + HttpUtils.encodeURLParam(dimensionsInfo) + "&revision=" + request.getRevision(); Map headers = new HashMap<>(); headers.put("x-environment", request.getEnvironment()); HttpRequest httpRequest = new HttpRequest(uri, headers, null, HttpRequest.GET); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); recordAndSendUnAuthorizedEvent(httpResponse, address); if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { Map> allConfigMap = HttpUtils.deserialize( httpResponse.getContent(), new TypeReference>>() { }); if (allConfigMap.get(REVISION) != null) { queryConfigurationsResponse.setRevision((String) allConfigMap.get(REVISION).get("version")); } if (allConfigMap.get(APPLICATION_CONFIG) != null) { configurations.putAll(allConfigMap.get(APPLICATION_CONFIG)); logConfigurationNames(APPLICATION_CONFIG, allConfigMap.get(APPLICATION_CONFIG)); } if (allConfigMap.get(buildDimensionsInfo(request, false)) != null) { configurations.putAll(allConfigMap.get(buildDimensionsInfo(request, false))); logConfigurationNames(buildDimensionsInfo(request, false), allConfigMap.get(buildDimensionsInfo(request, false))); } if (allConfigMap.get(buildDarkLaunchDimensionsInfo(request)) != null) { configurations.putAll(allConfigMap.get(buildDarkLaunchDimensionsInfo(request))); logConfigurationNames(buildDarkLaunchDimensionsInfo(request), allConfigMap.get(buildDarkLaunchDimensionsInfo(request))); } if (allConfigMap.get(dimensionsInfo) != null) { configurations.putAll(allConfigMap.get(dimensionsInfo)); logConfigurationNames(dimensionsInfo, allConfigMap.get(dimensionsInfo)); } queryConfigurationsResponse.setConfigurations(configurations); queryConfigurationsResponse.setChanged(true); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { queryConfigurationsResponse.setChanged(false); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { LOGGER.warn("rate limited, keep the local dimension [{}] configs unchanged.", dimensionsInfo); queryConfigurationsResponse.setChanged(false); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { throw new OperationException("Bad request for query configurations."); } else { throw new OperationException( "read response failed. status:" + httpResponse.getStatusCode() + "; message:" + httpResponse.getMessage() + "; content:" + httpResponse.getContent()); } } catch (IOException e) { addressManager.recordFailState(address); LOGGER.error("query configuration from {} failed, message={}", uri, e.getMessage()); throw new OperationException("", e); } } private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { LOGGER.warn("query configuration unauthorized from server [{}], message [{}]", address, response.getMessage()); addressManager.recordFailState(address); this.eventBus.post(new UnAuthorizedOperationEvent(address)); } else { addressManager.recordSuccessState(address); } } /** * Only the name of the new configuration item is printed. * No log is printed when the configuration content is updated. * * @param dimension dimension * @param configs configs */ private void logConfigurationNames(String dimension, Map configs) { if (CollectionUtils.isEmpty(configs)) { return; } List configNames = dimensionConfigNames.get(dimension); if (configNames == null) { configNames = new ArrayList<>(); } StringBuilder names = new StringBuilder(); for (String key : configs.keySet()) { if (configNames.contains(key)) { continue; } names.append(key).append(","); configNames.add(key); } if (names.isEmpty()) { return; } dimensionConfigNames.put(dimension, configNames); String fileNames = names.substring(0, names.length() - 1); LOGGER.info("pulling dimension [{}] configurations success, get config names: [{}].", dimension, fileNames); } @Override public void checkAddressAvailable(String address) { ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); } private String buildDimensionsInfo(QueryConfigurationsRequest request, boolean withVersion) { String result = request.getServiceName() + DEFAULT_APP_SEPARATOR + request.getApplication(); if (withVersion && !StringUtils.isEmpty(request.getVersion())) { result = result + DEFAULT_SERVICE_SEPARATOR + request .getVersion(); } return result; } private String buildDarkLaunchDimensionsInfo(QueryConfigurationsRequest request) { return DARK_LAUNCH + request.getApplication(); } } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterConfigurationChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; /** * This event is fired when configuration changed of config center. */ public class ConfigCenterConfigurationChangedEvent { private final Map added; private final Map deleted; private final Map updated; private Set changed; private ConfigCenterConfigurationChangedEvent(Map added, Map updated, Map deleted) { this.added = added; this.deleted = deleted; this.updated = updated; this.changed = new HashSet<>(); this.changed.addAll(added.keySet()); this.changed.addAll(updated.keySet()); this.changed.addAll(deleted.keySet()); } public static ConfigCenterConfigurationChangedEvent createIncremental(Map latest, Map last) { Map itemsCreated = new HashMap<>(); Map itemsDeleted = new HashMap<>(); Map itemsModified = new HashMap<>(); for (Map.Entry entry : latest.entrySet()) { String itemKey = entry.getKey(); if (!last.containsKey(itemKey)) { itemsCreated.put(itemKey, entry.getValue()); } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { itemsModified.put(itemKey, entry.getValue()); } } for (String itemKey : last.keySet()) { if (!latest.containsKey(itemKey)) { itemsDeleted.put(itemKey, null); } } ConfigCenterConfigurationChangedEvent event = ConfigCenterConfigurationChangedEvent .createIncremental(itemsCreated, itemsModified, itemsDeleted); return event; } public static ConfigCenterConfigurationChangedEvent createIncremental(Map added, Map updated, Map deleted) { return new ConfigCenterConfigurationChangedEvent(added, updated, deleted); } public static ConfigCenterConfigurationChangedEvent createIncremental(Map updated) { return new ConfigCenterConfigurationChangedEvent(new HashMap<>(), updated, new HashMap<>()); } public final Map getAdded() { return added; } public final Map getUpdated() { return updated; } public final Map getDeleted() { return deleted; } public final Set getChanged() { return changed; } } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.center.client.model.ConfigCenterConfiguration; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; import org.apache.servicecomb.config.common.ConfigConverter; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; public class ConfigCenterManager extends AbstractTask { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterManager.class); private final ConfigCenterClient configCenterClient; private final EventBus eventBus; private QueryConfigurationsRequest queryConfigurationsRequest; private final ConfigConverter configConverter; private final ConfigCenterConfiguration configCenterConfiguration; private final ConfigCenterAddressManager configCenterAddressManager; public ConfigCenterManager(ConfigCenterClient configCenterClient, EventBus eventBus, ConfigConverter configConverter, ConfigCenterConfiguration configCenterConfiguration, ConfigCenterAddressManager configCenterAddressManager) { super("config-center-configuration-task"); this.configCenterClient = configCenterClient; this.eventBus = eventBus; this.configConverter = configConverter; this.configCenterConfiguration = configCenterConfiguration; this.configCenterAddressManager = configCenterAddressManager; } public void setQueryConfigurationsRequest(QueryConfigurationsRequest queryConfigurationsRequest) { this.queryConfigurationsRequest = queryConfigurationsRequest; } public void startConfigCenterManager() { this.startTask(new PollConfigurationTask(0)); schedulerCheckAddressAvailable("cc-addr-check", new CheckConfigCenterAddressTask(), configCenterConfiguration.getRefreshIntervalInMillis()); } class PollConfigurationTask implements Task { int failCount; public PollConfigurationTask(int failCount) { this.failCount = failCount; } @Override public void execute() { try { QueryConfigurationsResponse response = configCenterClient.queryConfigurations(queryConfigurationsRequest, configCenterAddressManager.address()); if (response.isChanged()) { queryConfigurationsRequest.setRevision(response.getRevision()); Map lastData = configConverter.updateData(response.getConfigurations()); ConfigCenterConfigurationChangedEvent event = ConfigCenterConfigurationChangedEvent .createIncremental(configConverter.getCurrentData(), lastData); if (!event.getChanged().isEmpty()) { eventBus.post(event); } } startTask( new BackOffSleepTask(configCenterConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0))); } catch (Exception e) { LOGGER.warn("get configurations from ConfigCenter failed, and will try again, cause message: {}. current " + "fail does not affect the obtained historical configuration.", e.getCause().getMessage()); startTask(new BackOffSleepTask(failCount + 1, new PollConfigurationTask(failCount + 1))); } } } class CheckConfigCenterAddressTask implements Runnable { @Override public void run() { List isolationAddresses = configCenterAddressManager.getIsolationAddresses(); if (isolationAddresses.isEmpty()) { return; } for (String address : isolationAddresses) { configCenterClient.checkAddressAvailable(address); } } } } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client; import org.apache.servicecomb.config.common.exception.OperationException; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; public interface ConfigCenterOperation { /** * 根据查询条件查询配置项。 * @param request 查询的维度(project, application, serviceName, version) 和 revision 信息。 * @param address 查询的配置中心地址。 * @return 如果存在配置变更,返回全量的配置项, changed = true。 如果没有变更, 返回 null, changed = false, * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request, String address); /** * Check config center isolation address available * * @param address isolation address */ void checkAddressAvailable(String address); } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/ConfigCenterConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client.model; public class ConfigCenterConfiguration { private long refreshIntervalInMillis = 15000; public long getRefreshIntervalInMillis() { return refreshIntervalInMillis; } public ConfigCenterConfiguration setRefreshIntervalInMillis(long refreshIntervalInMillis) { this.refreshIntervalInMillis = refreshIntervalInMillis; return this; } } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client.model; public class QueryConfigurationsRequest { private String environment; private String application; private String serviceName; private String version; private String revision; public String getEnvironment() { return environment; } public QueryConfigurationsRequest setEnvironment(String environment) { this.environment = environment; return this; } public String getApplication() { return application; } public QueryConfigurationsRequest setApplication(String application) { this.application = application; return this; } public String getServiceName() { return serviceName; } public QueryConfigurationsRequest setServiceName(String serviceName) { this.serviceName = serviceName; return this; } public String getVersion() { return version; } public QueryConfigurationsRequest setVersion(String version) { this.version = version; return this; } public String getRevision() { return revision; } public QueryConfigurationsRequest setRevision(String revision) { this.revision = revision; return this; } } ================================================ FILE: clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.center.client.model; import java.util.Map; public class QueryConfigurationsResponse { private String revision; private boolean changed; private Map configurations; public String getRevision() { return revision; } public QueryConfigurationsResponse setRevision(String revision) { this.revision = revision; return this; } public boolean isChanged() { return changed; } public QueryConfigurationsResponse setChanged(boolean changed) { this.changed = changed; return this; } public Map getConfigurations() { return configurations; } public QueryConfigurationsResponse setConfigurations( Map configurations) { this.configurations = configurations; return this; } } ================================================ FILE: clients/config-common/pom.xml ================================================ clients org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-clients-common ServiceComb::Clients::Config Common org.apache.servicecomb http-client-common org.springframework spring-beans ================================================ FILE: clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.common; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ByteArrayResource; import org.springframework.util.CollectionUtils; public class ConfigConverter { private Map currentData = Collections.emptyMap(); private Map lastRawData; private final List fileSources; public ConfigConverter(List fileSources) { this.fileSources = fileSources; } public Map getLastRawData() { return this.lastRawData; } public Map getCurrentData() { return this.currentData; } public Map updateData(Map rawData) { Map lastData = this.currentData; this.lastRawData = rawData; if (CollectionUtils.isEmpty(fileSources)) { this.currentData = rawData; return lastData; } Map fileProperties = new HashMap<>(); fileSources.forEach(source -> { if (rawData.get(source) != null) { fileProperties.put(source, rawData.get(source)); } }); Map result = new HashMap<>(rawData.size()); result.putAll(rawData); fileProperties.forEach((k, v) -> result.putAll(createFileSource(v))); this.currentData = result; return lastData; } private Map createFileSource(Object v) { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(v.toString().getBytes(StandardCharsets.UTF_8))); return propertiesToMap(yamlFactory.getObject()); } @SuppressWarnings("unchecked") private Map propertiesToMap(Properties properties) { if (properties == null) { return Collections.emptyMap(); } Map result = new HashMap<>(); Enumeration keys = (Enumeration) properties.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Object value = properties.getProperty(key); result.put(key, value); } return result; } } ================================================ FILE: clients/config-common/src/main/java/org/apache/servicecomb/config/common/exception/OperationException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.common.exception; public class OperationException extends RuntimeException { private static final long serialVersionUID = 1L; public OperationException(String message) { super(message); } public OperationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: clients/config-kie-client/pom.xml ================================================ clients org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-kie-client ServiceComb::Clients::Kie Client org.apache.servicecomb http-client-common org.apache.servicecomb config-clients-common org.springframework spring-beans com.google.guava failureaccess test ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client; import com.google.common.eventbus.EventBus; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; import org.apache.http.HttpStatus; import org.apache.servicecomb.config.common.exception.OperationException; import org.apache.servicecomb.config.kie.client.model.ConfigConstants; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; import org.apache.servicecomb.config.kie.client.model.KVDoc; import org.apache.servicecomb.config.kie.client.model.KVResponse; import org.apache.servicecomb.config.kie.client.model.KieAddressManager; import org.apache.servicecomb.config.kie.client.model.KieConfiguration; import org.apache.servicecomb.config.kie.client.model.ValueType; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ByteArrayResource; import org.springframework.util.CollectionUtils; public class KieClient implements KieConfigOperation { private static final Logger LOGGER = LoggerFactory.getLogger(KieClient.class); private static final String ADDRESS_CHECK_PATH = "/v1/health"; protected HttpTransport httpTransport; protected String revision = "0"; private final KieAddressManager addressManager; private final KieConfiguration kieConfiguration; public static final String DEFAULT_KIE_API_VERSION = "v1"; private final Map> dimensionConfigNames = new HashMap<>(); private EventBus eventBus; public KieClient(KieAddressManager addressManager, HttpTransport httpTransport, KieConfiguration kieConfiguration) { this.httpTransport = httpTransport; this.addressManager = addressManager; this.kieConfiguration = kieConfiguration; } public void setEventBus(EventBus eventBus) { this.eventBus = eventBus; addressManager.setEventBus(eventBus); } @Override public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request, String address) { String url = buildUrl(request, address); try { if (kieConfiguration.isEnableLongPolling()) { url += "&wait=" + kieConfiguration.getPollingWaitInSeconds() + "s"; } HttpRequest httpRequest = new HttpRequest(url, null, null, HttpRequest.GET); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); recordAndSendUnAuthorizedEvent(httpResponse, address); ConfigurationsResponse configurationsResponse = new ConfigurationsResponse(); if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { revision = httpResponse.getHeader("X-Kie-Revision"); KVResponse allConfigList = HttpUtils.deserialize(httpResponse.getContent(), KVResponse.class); logConfigurationNames(request.getLabelsQuery(), allConfigList.getData()); Map configurations = getConfigByLabel(allConfigList); configurationsResponse.setConfigurations(configurations); configurationsResponse.setChanged(true); configurationsResponse.setRevision(revision); return configurationsResponse; } if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { throw new OperationException("Bad request for query configurations."); } if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { configurationsResponse.setChanged(false); return configurationsResponse; } if (httpResponse.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { LOGGER.warn("rate limited, keep the local dimension [{}] configs unchanged.", request.getLabelsQuery()); configurationsResponse.setChanged(false); return configurationsResponse; } throw new OperationException( "read response failed. status:" + httpResponse.getStatusCode() + "; message:" + httpResponse.getMessage() + "; content:" + httpResponse.getContent()); } catch (Exception e) { addressManager.recordFailState(address); LOGGER.error("query configuration from {} failed, message={}", url, e.getMessage()); throw new OperationException("read response failed. ", e); } } private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { LOGGER.warn("query configuration unauthorized from server [{}], message [{}]", address, response.getMessage()); addressManager.recordFailState(address); this.eventBus.post(new UnAuthorizedOperationEvent(address)); } else { addressManager.recordSuccessState(address); } } /** * Only the name of the new configuration item is printed. * No log is printed when the configuration content is updated. * * @param dimension dimension * @param data configs-data */ private void logConfigurationNames(String dimension, List data) { if (CollectionUtils.isEmpty(data)) { return; } List configNames = dimensionConfigNames.get(dimension); if (configNames == null) { configNames = new ArrayList<>(); } StringBuilder names = new StringBuilder(); for (KVDoc doc : data) { if (configNames.contains(doc.getKey())) { continue; } names.append(doc.getKey()).append(","); configNames.add(doc.getKey()); } if (names.isEmpty()) { return; } dimensionConfigNames.put(dimension, configNames); String fileNames = names.substring(0, names.length() - 1); LOGGER.info("pulling dimension [{}] configurations success, get config names: [{}].", dimension, fileNames); } @Override public void checkAddressAvailable(String address) { ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); } private String buildUrl(ConfigurationsRequest request, String currentAddress) { StringBuilder sb = new StringBuilder(); sb.append(currentAddress); sb.append("/"); sb.append(DEFAULT_KIE_API_VERSION); sb.append("/"); sb.append(kieConfiguration.getProject()); sb.append("/kie/kv?"); sb.append(request.getLabelsQuery()); sb.append("&revision="); sb.append(request.getRevision()); if (request.isWithExact()) { sb.append("&match=exact"); } return sb.toString(); } private Map getConfigByLabel(KVResponse resp) { Map resultMap = new HashMap<>(); resp.getData().stream() .sorted(Comparator.comparing(KVDoc::getUpdateTime, Comparator.nullsFirst(Comparator.naturalOrder()))) .filter(doc -> doc.getStatus() == null || ConfigConstants.STATUS_ENABLED.equalsIgnoreCase(doc.getStatus())) .map(this::processValueType) .collect(Collectors.toList()) .forEach(resultMap::putAll); return resultMap; } private Map processValueType(KVDoc kvDoc) { ValueType valueType; try { valueType = ValueType.valueOf(kvDoc.getValueType()); } catch (IllegalArgumentException e) { throw new OperationException("value type not support [" + kvDoc.getValue() + "]"); } Properties properties = new Properties(); Map kvMap = new HashMap<>(); try { switch (valueType) { case yml: case yaml: YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(kvDoc.getValue().getBytes(StandardCharsets.UTF_8))); return toMap(yamlFactory.getObject()); case properties: properties.load(new StringReader(kvDoc.getValue())); return toMap(properties); case text: case string: default: kvMap.put(kvDoc.getKey(), kvDoc.getValue()); return kvMap; } } catch (Exception e) { LOGGER.error("read config failed", e); } return Collections.emptyMap(); } @SuppressWarnings("unchecked") private Map toMap(Properties properties) { if (properties == null) { return Collections.emptyMap(); } Map result = new HashMap<>(); Enumeration keys = (Enumeration) properties.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Object value = properties.getProperty(key); result.put(key, value); } return result; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import org.apache.servicecomb.config.common.ConfigConverter; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequestFactory; import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; import org.apache.servicecomb.config.kie.client.model.KieAddressManager; import org.apache.servicecomb.config.kie.client.model.KieConfiguration; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; public class KieConfigManager extends AbstractTask { private static final Logger LOGGER = LoggerFactory.getLogger(KieConfigManager.class); private static final long LONG_POLLING_INTERVAL = 1000; private final KieConfigOperation configKieClient; private final EventBus eventBus; private final ConfigConverter configConverter; private final List configurationsRequests; private final KieConfiguration kieConfiguration; private final KieAddressManager kieAddressManager; public KieConfigManager(KieConfigOperation configKieClient, EventBus eventBus, KieConfiguration kieConfiguration, ConfigConverter configConverter, KieAddressManager kieAddressManager) { super("config-center-configuration-task"); this.configurationsRequests = ConfigurationsRequestFactory.buildConfigurationRequests(kieConfiguration); this.configurationsRequests.sort(ConfigurationsRequest::compareTo); this.configKieClient = configKieClient; this.eventBus = eventBus; this.configConverter = configConverter; this.kieConfiguration = kieConfiguration; this.kieAddressManager = kieAddressManager; } public void firstPull() { Map data = new HashMap<>(); try { firstQueryConfigurations(data); } catch (Exception e) { if (this.kieConfiguration.isFirstPullRequired()) { throw e; } else { LOGGER.warn("first pull failed!"); } } } private void firstQueryConfigurations(Map data) { for (int i = 0; i < 3;) { String address = kieAddressManager.address(); try { this.configurationsRequests.forEach(r -> { r.setRevision(ConfigurationsRequest.INITIAL_REVISION); ConfigurationsResponse response = configKieClient.queryConfigurations(r, address); if (response.isChanged()) { r.setRevision(response.getRevision()); r.setLastRawData(response.getConfigurations()); data.putAll(response.getConfigurations()); } }); this.configConverter.updateData(data); break; } catch (Exception e) { if (i == 2) { throw e; } LOGGER.warn("firstQueryConfigurations failed, config address {} and ignore {}", address, e.getMessage()); } i++; } } private void onDataChanged() { Map latestData = new HashMap<>(); this.configurationsRequests.forEach(r -> latestData.putAll(r.getLastRawData())); Map lastData = configConverter.updateData(latestData); KieConfigurationChangedEvent event = KieConfigurationChangedEvent .createIncremental(configConverter.getCurrentData(), lastData); if (!event.getChanged().isEmpty()) { eventBus.post(event); } } @Override protected void initTaskPool(String taskName) { this.taskPool = Executors.newFixedThreadPool(3, (task) -> new Thread(task, taskName)); } public void startConfigKieManager() { this.configurationsRequests.forEach((t) -> this.startTask(new PollConfigurationTask(0, t))); schedulerCheckAddressAvailable("kie-addr-check", new CheckKieAddressTask(), kieConfiguration.getRefreshIntervalInMillis()); } class PollConfigurationTask implements Task { final int failCount; ConfigurationsRequest configurationsRequest; public PollConfigurationTask(int failCount, ConfigurationsRequest configurationsRequest) { this.failCount = failCount; this.configurationsRequest = configurationsRequest; } @Override public void execute() { try { ConfigurationsResponse response = configKieClient.queryConfigurations(configurationsRequest, kieAddressManager.address()); if (response.isChanged()) { configurationsRequest.setRevision(response.getRevision()); configurationsRequest.setLastRawData(response.getConfigurations()); onDataChanged(); } if (KieConfigManager.this.kieConfiguration.isEnableLongPolling()) { startTask( new BackOffSleepTask(LONG_POLLING_INTERVAL, new PollConfigurationTask(0, this.configurationsRequest))); } else { startTask(new BackOffSleepTask(kieConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0, this.configurationsRequest))); } } catch (Exception e) { LOGGER.warn("get configurations from KieConfigCenter failed, and will try again, cause message: {}. current " + "fail does not affect the obtained historical configuration.", e.getCause().getMessage()); startTask( new BackOffSleepTask(failCount + 1, new PollConfigurationTask(failCount + 1, this.configurationsRequest))); } } } class CheckKieAddressTask implements Runnable { @Override public void run() { List isolationAddresses = kieAddressManager.getIsolationAddresses(); if (isolationAddresses.isEmpty()) { return; } for (String address : isolationAddresses) { configKieClient.checkAddressAvailable(address); } } } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client; import org.apache.servicecomb.config.common.exception.OperationException; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; //此处支持配置中心扩展 public interface KieConfigOperation { /** * 根据查询条件查询配置项。 * @param request 查询的维度(project, application, serviceName, version) 和 revision 信息。 * @param address 查询的配置中心地址。 * @return 如果存在配置变更,返回全量的配置项, changed = true。 如果没有变更, 返回 null, changed = false, * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ ConfigurationsResponse queryConfigurations(ConfigurationsRequest request, String address); /** * Check kie isolation address available * * @param address isolation address */ void checkAddressAvailable(String address); } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigurationChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; /** * This event is fired when configuration changed of kie. */ public class KieConfigurationChangedEvent { private final Map added; private final Map deleted; private final Map updated; private Set changed; private KieConfigurationChangedEvent(Map added, Map updated, Map deleted) { this.added = added; this.deleted = deleted; this.updated = updated; this.changed = new HashSet<>(); this.changed.addAll(added.keySet()); this.changed.addAll(updated.keySet()); this.changed.addAll(deleted.keySet()); } public static KieConfigurationChangedEvent createIncremental(Map latest, Map last) { Map itemsCreated = new HashMap<>(); Map itemsDeleted = new HashMap<>(); Map itemsModified = new HashMap<>(); for (Map.Entry entry : latest.entrySet()) { String itemKey = entry.getKey(); if (!last.containsKey(itemKey)) { itemsCreated.put(itemKey, entry.getValue()); } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { itemsModified.put(itemKey, entry.getValue()); } } for (String itemKey : last.keySet()) { if (!latest.containsKey(itemKey)) { itemsDeleted.put(itemKey, null); } } KieConfigurationChangedEvent event = KieConfigurationChangedEvent .createIncremental(itemsCreated, itemsModified, itemsDeleted); return event; } public static KieConfigurationChangedEvent createIncremental(Map added, Map updated, Map deleted) { return new KieConfigurationChangedEvent(added, updated, deleted); } public static KieConfigurationChangedEvent createIncremental(Map updated) { return new KieConfigurationChangedEvent(new HashMap<>(), updated, new HashMap<>()); } public final Map getAdded() { return added; } public final Map getUpdated() { return updated; } public final Map getDeleted() { return deleted; } public final Set getChanged() { return changed; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; public class ConfigConstants { public static final String LABEL_ENV = "environment"; public static final String LABEL_APP = "app"; public static final String LABEL_SERVICE = "service"; public static final String LABEL_VERSION = "version"; public static final String STATUS_ENABLED = "enabled"; public static final String KEY_PROJECT = "project"; // ###### kie config center polling configuration############### // public static final String KEY_ENABLELONGPOLLING = "enableLongPolling"; public static final String KEY_POLLINGWAITSEC = "pollingWaitInSeconds"; } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import java.util.HashMap; import java.util.Map; public class ConfigurationsRequest implements Comparable { public static final String INITIAL_REVISION = "-1"; private int order; private String revision = INITIAL_REVISION; private boolean withExact; private String labelsQuery; private Map lastRawData = new HashMap<>(); public int getOrder() { return order; } public ConfigurationsRequest setOrder(int order) { this.order = order; return this; } public String getRevision() { return revision; } public ConfigurationsRequest setRevision(String revision) { this.revision = revision; return this; } public boolean isWithExact() { return withExact; } public ConfigurationsRequest setWithExact(boolean withExact) { this.withExact = withExact; return this; } public String getLabelsQuery() { return labelsQuery; } public ConfigurationsRequest setLabelsQuery(String labelsQuery) { this.labelsQuery = labelsQuery; return this; } public Map getLastRawData() { return lastRawData; } public ConfigurationsRequest setLastRawData(Map lastRawData) { this.lastRawData = lastRawData; return this; } @Override public int compareTo(ConfigurationsRequest o) { // Higher priority, query the last return o.getOrder() - this.order; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.http.client.common.HttpUtils; public class ConfigurationsRequestFactory { private static final String KEY_APP = "app"; private static final String KEY_ENVIRONMENT = "environment"; private static final String KEY_SERVICE = "service"; private static final String KEY_VERSION = "version"; private static final int CUSTOM_ORDER = 100; private static final int VERSION_ORDER = 200; private static final int SERVICE_ORDER = 300; private static final int APP_ORDER = 400; public static List buildConfigurationRequests(KieConfiguration configuration) { List result = new ArrayList<>(); if (configuration.isEnableAppConfig()) { result.add(createAppConfigurationsRequest(configuration)); } if (configuration.isEnableServiceConfig()) { result.add(createServiceConfigurationsRequest(configuration)); } if (configuration.isEnableVersionConfig()) { result.add(createVersionConfigurationsRequest(configuration)); } if (configuration.isEnableCustomConfig()) { result.add(createCustomConfigurationsRequest(configuration)); } return result; } private static ConfigurationsRequest createAppConfigurationsRequest(KieConfiguration configuration) { return new ConfigurationsRequest() .setOrder(APP_ORDER) .setWithExact(true) .setLabelsQuery(buildLabelQuery(buildLabelQueryItem(KEY_APP, configuration.getAppName()), buildLabelQueryItem(KEY_ENVIRONMENT, configuration.getEnvironment()))); } private static ConfigurationsRequest createServiceConfigurationsRequest(KieConfiguration configuration) { return new ConfigurationsRequest() .setOrder(SERVICE_ORDER) .setWithExact(true) .setLabelsQuery(buildLabelQuery(buildLabelQueryItem(KEY_APP, configuration.getAppName()), buildLabelQueryItem(KEY_SERVICE, configuration.getServiceName()), buildLabelQueryItem(KEY_ENVIRONMENT, configuration.getEnvironment()))); } private static ConfigurationsRequest createVersionConfigurationsRequest(KieConfiguration configuration) { return new ConfigurationsRequest() .setOrder(VERSION_ORDER) .setWithExact(true) .setLabelsQuery(buildLabelQuery(buildLabelQueryItem(KEY_APP, configuration.getAppName()), buildLabelQueryItem(KEY_SERVICE, configuration.getServiceName()), buildLabelQueryItem(KEY_ENVIRONMENT, configuration.getEnvironment()), buildLabelQueryItem(KEY_VERSION, configuration.getVersion()))); } private static ConfigurationsRequest createCustomConfigurationsRequest(KieConfiguration configuration) { return new ConfigurationsRequest() .setOrder(CUSTOM_ORDER) .setWithExact(false) .setLabelsQuery( buildLabelQuery(buildLabelQueryItem(configuration.getCustomLabel(), configuration.getCustomLabelValue()))); } private static String buildLabelQuery(String... labels) { StringBuilder result = new StringBuilder(); for (String label : labels) { result.append(label); result.append("&"); } return result.toString(); } private static String buildLabelQueryItem(String key, String value) { try { return "label=" + HttpUtils.encodeURLParam(key + ":" + value); } catch (IOException e) { throw new IllegalArgumentException("unexpected param", e); } } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import java.util.Map; public class ConfigurationsResponse { private String revision; private boolean changed; private Map configurations; public String getRevision() { return revision; } public ConfigurationsResponse setRevision(String revision) { this.revision = revision; return this; } public boolean isChanged() { return changed; } public ConfigurationsResponse setChanged(boolean changed) { this.changed = changed; return this; } public Map getConfigurations() { return configurations; } public ConfigurationsResponse setConfigurations( Map configurations) { this.configurations = configurations; return this; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import com.fasterxml.jackson.annotation.JsonAlias; import java.util.HashMap; import java.util.Map; public class KVDoc { private String id; private String check; private String domain; private String key; @JsonAlias("label_id") private String labelId; private Map labels = new HashMap<>(); private String value; @JsonAlias("value_type") private String valueType; private String status; @JsonAlias("update_time") private long updateTime; public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getCheck() { return check; } public String getDomain() { return domain; } public String getLabelId() { return labelId; } public Map getLabels() { return labels; } public String getValue() { return value; } public void setCheck(String check) { this.check = check; } public void setDomain(String domain) { this.domain = domain; } public void setLabelId(String labelId) { this.labelId = labelId; } public void setLabels(Map labels) { this.labels = labels; } public void setValueType(String valueType) { this.valueType = valueType; } public void setValue(String value) { this.value = value; } public String getValueType() { return valueType; } public long getUpdateTime() { return updateTime; } public void setUpdateTime(long updateTime) { this.updateTime = updateTime; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import java.util.List; public class KVResponse { private List data; private LabelDocResponse label; private Integer total; public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } public List getData() { return data; } public LabelDocResponse getLabel() { return label; } public void setData(List data) { this.data = data; } public void setLabel(LabelDocResponse label) { this.label = label; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import java.util.List; import org.apache.servicecomb.http.client.common.AbstractAddressManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class KieAddressManager extends AbstractAddressManager { public KieAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { super(addresses, region, availableZone); eventBus.register(this); } @Subscribe public void onRefreshEndpointEvent(RefreshEndpointEvent event) { refreshEndpoint(event, RefreshEndpointEvent.KIE_NAME); } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; public class KieConfiguration { private boolean enableLongPolling; private int pollingWaitInSeconds; private int refreshIntervalInMillis = 15000; private String project; private String appName; private String serviceName; private String environment; private String version; private boolean enableAppConfig; private boolean enableServiceConfig; private boolean enableVersionConfig; private boolean enableCustomConfig; private String customLabelValue; private String customLabel; private boolean firstPullRequired; public String getAppName() { return appName; } public KieConfiguration setAppName(String appName) { this.appName = appName; return this; } public String getServiceName() { return serviceName; } public KieConfiguration setServiceName(String serviceName) { this.serviceName = serviceName; return this; } public String getEnvironment() { return environment; } public KieConfiguration setEnvironment(String environment) { this.environment = environment; return this; } public String getCustomLabelValue() { return customLabelValue; } public KieConfiguration setCustomLabelValue(String customLabelValue) { this.customLabelValue = customLabelValue; return this; } public boolean isEnableAppConfig() { return enableAppConfig; } public KieConfiguration setEnableAppConfig(boolean enableAppConfig) { this.enableAppConfig = enableAppConfig; return this; } public boolean isEnableServiceConfig() { return enableServiceConfig; } public KieConfiguration setEnableServiceConfig(boolean enableServiceConfig) { this.enableServiceConfig = enableServiceConfig; return this; } public boolean isEnableCustomConfig() { return enableCustomConfig; } public KieConfiguration setEnableCustomConfig(boolean enableCustomConfig) { this.enableCustomConfig = enableCustomConfig; return this; } public String getCustomLabel() { return customLabel; } public KieConfiguration setCustomLabel(String customLabel) { this.customLabel = customLabel; return this; } public boolean isEnableLongPolling() { return enableLongPolling; } public KieConfiguration setEnableLongPolling(boolean enableLongPolling) { this.enableLongPolling = enableLongPolling; return this; } public boolean isEnableVersionConfig() { return enableVersionConfig; } public KieConfiguration setEnableVersionConfig(boolean enableVersionConfig) { this.enableVersionConfig = enableVersionConfig; return this; } public int getPollingWaitInSeconds() { return pollingWaitInSeconds; } public KieConfiguration setPollingWaitInSeconds(int pollingWaitInSeconds) { this.pollingWaitInSeconds = pollingWaitInSeconds; return this; } public String getProject() { return project; } public KieConfiguration setProject(String project) { this.project = project; return this; } public boolean isFirstPullRequired() { return firstPullRequired; } public KieConfiguration setFirstPullRequired(boolean firstPullRequired) { this.firstPullRequired = firstPullRequired; return this; } public int getRefreshIntervalInMillis() { return refreshIntervalInMillis; } public KieConfiguration setRefreshIntervalInMillis(int refreshIntervallnMillis) { this.refreshIntervalInMillis = refreshIntervallnMillis; return this; } public String getVersion() { return version; } public KieConfiguration setVersion(String version) { this.version = version; return this; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/LabelDocResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import com.fasterxml.jackson.annotation.JsonAlias; import java.util.HashMap; import java.util.Map; public class LabelDocResponse { @JsonAlias("label_id") private String labelId; private Map labels = new HashMap<>(); public String getLabelId() { return labelId; } public Map getLabels() { return labels; } public void setLabelId(String labelId) { this.labelId = labelId; } public void setLabels(Map labels) { this.labels = labels; } } ================================================ FILE: clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; public enum ValueType { yml, yaml, ini, string, text, json, properties, xml } ================================================ FILE: clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Map; class ConfigurationsRequestTest { @Test void getLastRawData() { ConfigurationsRequest configurationsRequest = new ConfigurationsRequest(); Map lastRawData = configurationsRequest.getLastRawData(); Assertions.assertEquals(lastRawData.size(), 0); } } ================================================ FILE: clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.client.model; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import com.google.common.eventbus.EventBus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class KieAddressManagerTest { private static final List addresses = new ArrayList<>(); private static KieAddressManager addressManager1; @Test public void kieAddressManagerTest() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); Assertions.assertNotNull(addressManager1); List addresses = addressManager1.getAddresses(); Assertions.assertEquals(2, addresses.size()); Assertions.assertEquals("http://127.0.0.1:30103", addresses.get(0)); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); } @Test public void onRefreshEndpointEvent() { List addressAZ = new ArrayList<>(); addressAZ.add("http://127.0.0.3:30100"); List addressRG = new ArrayList<>(); addressRG.add("http://127.0.0.4:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "KIE"); addressManager1.refreshEndpoint(event, "KIE"); List availableZone = addressManager1.getAvailableZone(); Assertions.assertEquals("http://127.0.0.3:30100", availableZone.get(0)); List availableRegion = addressManager1.getAvailableRegion(); Assertions.assertEquals("http://127.0.0.4:30100", availableRegion.get(0)); } } ================================================ FILE: clients/dashboard-client/pom.xml ================================================ clients org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 dashboard-client ServiceComb::Clients::Dashboard Client org.apache.servicecomb http-client-common org.springframework spring-beans com.google.guava failureaccess test ================================================ FILE: clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.dashboard.client; import java.util.List; import org.apache.servicecomb.http.client.common.AbstractAddressManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class DashboardAddressManager extends AbstractAddressManager { public DashboardAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { super(addresses, region, availableZone); eventBus.register(this); } @Subscribe public void onRefreshEndpointEvent(RefreshEndpointEvent event) { refreshEndpoint(event, RefreshEndpointEvent.CSE_MONITORING_NAME); } } ================================================ FILE: clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.dashboard.client; import org.apache.http.HttpStatus; import org.apache.servicecomb.dashboard.client.model.MonitorData; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DashboardClient implements DashboardOperation { private static final Logger LOGGER = LoggerFactory.getLogger(DashboardClient.class); protected HttpTransport httpTransport; private final DashboardAddressManager addressManager; public DashboardClient(DashboardAddressManager addressManager, HttpTransport httpTransport) { this.httpTransport = httpTransport; this.addressManager = addressManager; } @Override public void sendData(String url, MonitorData data) { String address = addressManager.address(); try { HttpRequest httpRequest = new HttpRequest(address + url, null, HttpUtils.serialize(data), HttpRequest.POST); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); if (httpResponse.getStatusCode() != HttpStatus.SC_OK) { LOGGER.error("send data to [{}] failed, status code [{}], message [{}]", url, httpResponse.getStatusCode() , httpResponse.getContent()); } } catch (Exception e) { LOGGER.error("send data to [{}] failed", url, e); } } } ================================================ FILE: clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.dashboard.client; import org.apache.servicecomb.dashboard.client.model.MonitorData; public interface DashboardOperation { void sendData(String url, MonitorData monitorData); } ================================================ FILE: clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/InterfaceInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.dashboard.client.model; public class InterfaceInfo { private String name; private String desc; private double qps; private int latency; private int l995; private int l99; private int l90; private int l75; private int l50; private int l25; private int l5; private double rate; private double failureRate; private long total; private boolean isCircuitBreakerOpen; private long failure; private long shortCircuited; private long semaphoreRejected; private long threadPoolRejected; private long countTimeout; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public double getQps() { return qps; } public void setQps(double qps) { this.qps = qps; } public int getLatency() { return latency; } public void setLatency(int latency) { this.latency = latency; } public int getL995() { return l995; } public void setL995(int l995) { this.l995 = l995; } public int getL99() { return l99; } public void setL99(int l99) { this.l99 = l99; } public int getL90() { return l90; } public void setL90(int l90) { this.l90 = l90; } public int getL75() { return l75; } public void setL75(int l75) { this.l75 = l75; } public int getL50() { return l50; } public void setL50(int l50) { this.l50 = l50; } public int getL25() { return l25; } public void setL25(int l25) { this.l25 = l25; } public int getL5() { return l5; } public void setL5(int l5) { this.l5 = l5; } public double getRate() { return rate; } public void setRate(double rate) { this.rate = rate; } public double getFailureRate() { return failureRate; } public void setFailureRate(double failureRate) { this.failureRate = failureRate; } public long getTotal() { return total; } public void setTotal(long total) { this.total = total; } public boolean isCircuitBreakerOpen() { return isCircuitBreakerOpen; } public void setCircuitBreakerOpen(boolean circuitBreakerOpen) { isCircuitBreakerOpen = circuitBreakerOpen; } public long getFailure() { return failure; } public void setFailure(long failure) { this.failure = failure; } public long getShortCircuited() { return shortCircuited; } public void setShortCircuited(long shortCircuited) { this.shortCircuited = shortCircuited; } public long getSemaphoreRejected() { return semaphoreRejected; } public void setSemaphoreRejected(long semaphoreRejected) { this.semaphoreRejected = semaphoreRejected; } public long getThreadPoolRejected() { return threadPoolRejected; } public void setThreadPoolRejected(long threadPoolRejected) { this.threadPoolRejected = threadPoolRejected; } public long getCountTimeout() { return countTimeout; } public void setCountTimeout(long countTimeout) { this.countTimeout = countTimeout; } } ================================================ FILE: clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/MonitorData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.dashboard.client.model; import java.util.ArrayList; import java.util.List; import java.util.Map; public class MonitorData { public static final double PERCENTAGE_995 = 99.5; public static final double PERCENTAGE_99 = 99; public static final double PERCENTAGE_90 = 90; public static final double PERCENTAGE_75 = 75; public static final double PERCENTAGE_50 = 50; public static final double PERCENTAGE_25 = 25; public static final double PERCENTAGE_5 = 5; public static final int SCALE_VAL = 1; public static final double DEFAULT_SUCCESS_RATE = 1.0d; public static final int CONVERSION = 1000; private String appId; private String version; private String name; private String serviceId; private String environment; private String instance; private String instanceId; private int thread; private double cpu; private double loadAverage; private long uptime; private Map memory; private List interfaces = new ArrayList<>(); private Map customs; public void addInterfaceInfo(InterfaceInfo interfaceInfo) { interfaces.add(interfaceInfo); } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public String getInstance() { return instance; } public void setInstance(String instance) { this.instance = instance; } public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public int getThreadCount() { return thread; } public void setThreadCount(int threadCount) { this.thread = threadCount; } public double getCpu() { return cpu; } public void setCpu(double cpu) { this.cpu = cpu; } public double getLoadAverage() { return loadAverage; } public void setLoadAverage(double loadAverage) { this.loadAverage = loadAverage; } public long getUptime() { return uptime; } public void setUptime(long uptime) { this.uptime = uptime; } public Map getMemory() { return memory; } public void setMemory(Map memory) { this.memory = memory; } public List getInterfaces() { return interfaces; } public void setInterfaces(List interfaces) { this.interfaces = interfaces; } public Map getCustoms() { return customs; } public void setCustoms(Map customs) { this.customs = customs; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } } ================================================ FILE: clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.dashboard.client; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import com.google.common.eventbus.EventBus; class AddressManagerTest { private static final List addresses = new ArrayList<>(); private static DashboardAddressManager addressManager1; @Test public void kieAddressManagerTest() throws IllegalAccessException, NoSuchFieldException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); Assertions.assertNotNull(addressManager1); List addresses = addressManager1.getAddresses(); Assertions.assertEquals(2, addresses.size()); Assertions.assertEquals("http://127.0.0.1:30103", addresses.get(0)); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); } @Test public void onRefreshEndpointEvent() { List addressAZ = new ArrayList<>(); addressAZ.add("http://127.0.0.3:30100"); List addressRG = new ArrayList<>(); addressRG.add("http://127.0.0.4:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseMonitoring"); addressManager1.refreshEndpoint(event, "CseMonitoring"); List availableZone = addressManager1.getAvailableZone(); Assertions.assertEquals("http://127.0.0.3:30100", availableZone.get(0)); List availableRegion = addressManager1.getAvailableRegion(); Assertions.assertEquals("http://127.0.0.4:30100", availableRegion.get(0)); } } ================================================ FILE: clients/http-client-common/pom.xml ================================================ clients org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 http-client-common ServiceComb::Clients::HTTP Client Common org.apache.httpcomponents httpclient org.slf4j slf4j-api com.fasterxml.jackson.core jackson-databind org.apache.commons commons-lang3 com.google.guava guava org.apache.servicecomb foundation-ssl org.apache.servicecomb foundation-spi org.java-websocket Java-WebSocket ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/DefaultRequestAuthHeaderProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.auth; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.auth.SignRequest; public class DefaultRequestAuthHeaderProvider implements RequestAuthHeaderProvider { @Override public Map loadAuthHeader(SignRequest signRequest) { return new HashMap<>(0); } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/RequestAuthHeaderProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.auth; import java.util.Map; import org.apache.servicecomb.foundation.auth.SignRequest; public interface RequestAuthHeaderProvider { Map loadAuthHeader(SignRequest signRequest); } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.http.client.event.EngineConnectChangedEvent; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.EventBus; public class AbstractAddressManager { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAddressManager.class); public static final String DEFAULT_PROJECT = "default"; public static final String V4_PREFIX = "/v4/"; private static final String V3_PREFIX = "/v3/"; private static final String ZONE = "availableZone"; private static final String REGION = "region"; private static final int ISOLATION_THRESHOLD = 3; private volatile List addresses = new ArrayList<>(); // when all addresses are isolation, it will use this for polling. private final List defaultAddress = new ArrayList<>(); private final List defaultIsolationAddress = new ArrayList<>(); private int index; private String projectName; // recording continuous times of failure of an address. private final Map addressFailureStatus = new ConcurrentHashMap<>(); private volatile List availableZone = new ArrayList<>(); private final List isolationZoneAddress = new ArrayList<>(); private volatile List availableRegion = new ArrayList<>(); private final List isolationRegionAddress = new ArrayList<>(); private volatile boolean addressAutoRefreshed = false; private final Object lock = new Object(); private final Random random = new Random(); private EventBus eventBus; public AbstractAddressManager(List addresses, String ownRegion, String ownAvailableZone) { this.projectName = DEFAULT_PROJECT; parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, false); this.index = !addresses.isEmpty() ? getRandomIndex() : 0; } /** * address support config with region/availableZone info, to enable engine affinity calls during startup * address may be like: * https://192.168.20.13:30110?region=region1&availableZone=az * https://192.168.20.13:30100?region=region1&availableZone=az * When address have no datacenter information, roundRobin using address * * @param addresses engine addresses * @param ownRegion microservice region * @param ownAvailableZone microservice zone * @param isFormat is need format */ private void parseAndInitAddresses(List addresses, String ownRegion, String ownAvailableZone, boolean isFormat) { if (CollectionUtils.isEmpty(addresses)) { return; } List tempList = new ArrayList<>(); addressAutoRefreshed = addresses.stream().anyMatch(addr -> addr.contains(ZONE) || addr.contains(REGION)); for (String address : addresses) { // Compatible IpPortManager init address is 127.0.0.1:30100 if (!address.startsWith("http")) { tempList.add(address); continue; } URLEndPoint endpoint = new URLEndPoint(address); tempList.add(endpoint.toString()); buildAffinityAddress(endpoint, ownRegion, ownAvailableZone); } this.addresses.addAll(isFormat ? this.transformAddress(tempList) : tempList); this.defaultAddress.addAll(isFormat ? this.transformAddress(tempList) : tempList); } private void buildAffinityAddress(URLEndPoint endpoint, String ownRegion, String ownAvailableZone) { if (addressAutoRefreshed) { if (regionAndAZMatch(ownRegion, ownAvailableZone, endpoint.getFirst(REGION), endpoint.getFirst(ZONE))) { availableZone.add(endpoint.toString()); } else { availableRegion.add(endpoint.toString()); } } } private boolean regionAndAZMatch(String ownRegion, String ownAvailableZone, String engineRegion, String engineAvailableZone) { return ownRegion.equalsIgnoreCase(engineRegion) && ownAvailableZone.equals(engineAvailableZone); } public AbstractAddressManager(String projectName, List addresses, String ownRegion, String ownAvailableZone) { this.projectName = StringUtils.isEmpty(projectName) ? DEFAULT_PROJECT : projectName; parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, true); this.index = !addresses.isEmpty() ? getRandomIndex() : 0; } private int getRandomIndex() { return random.nextInt(addresses.size()); } public void refreshEndpoint(RefreshEndpointEvent event, String key) { if (null == event || !event.getName().equals(key)) { return; } availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList()); availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList()); addressAutoRefreshed = true; } protected String normalizeUri(String endpoint) { return new URLEndPoint(endpoint).toString(); } @VisibleForTesting Map getAddressFailureStatus() { return addressFailureStatus; } public List getAddresses() { return addresses; } public List getAvailableZone() { return availableZone; } public List getAvailableRegion() { return availableRegion; } public String formatUrl(String url, boolean absoluteUrl, String address) { return absoluteUrl ? address + url : formatAddress(address) + url; } public boolean sslEnabled() { return address().startsWith("https://"); } protected List transformAddress(List addresses) { return addresses.stream().map(this::formatAddress).collect(Collectors.toList()); } protected String formatAddress(String address) { try { return getUrlPrefix(address) + HttpUtils.encodeURLParam(this.projectName); } catch (Exception e) { throw new IllegalStateException("not possible"); } } protected String getUrlPrefix(String address) { return address + V3_PREFIX; } public String address() { if (!addressAutoRefreshed) { return getDefaultAddress(); } else { return getAvailableZoneAddress(); } } private String getDefaultAddress() { if (!addresses.isEmpty()) { return getCurrentAddress(addresses); } LOGGER.warn("all addresses are isolation, please check server status."); // when all addresses are isolation, it will use all default address for polling. return getCurrentAddress(defaultAddress); } private String getAvailableZoneAddress() { List zoneOrRegionAddress = getZoneOrRegionAddress(); if (!zoneOrRegionAddress.isEmpty()) { return getCurrentAddress(zoneOrRegionAddress); } LOGGER.warn("all auto discovery addresses are isolation, please check server status."); // when all available address are isolation, it will use config addresses for polling. return getCurrentAddress(addresses); } private String getCurrentAddress(List addresses) { synchronized (this) { this.index++; if (this.index >= addresses.size()) { this.index = 0; } return addresses.get(index); } } private List getZoneOrRegionAddress() { List results = new ArrayList<>(); if (!availableZone.isEmpty()) { results.addAll(availableZone); } else { results.addAll(availableRegion); } return results; } public void recordSuccessState(String address) { resetFailureStatus(address); if (addressAutoRefreshed) { if (isolationZoneAddress.remove(address)) { LOGGER.warn("restore same region address [{}]", address); if (eventBus != null && availableZone.isEmpty()) { eventBus.post(new EngineConnectChangedEvent()); } availableZone.add(address); return; } if (isolationRegionAddress.remove(address)) { LOGGER.warn("restore same zone address [{}]", address); availableRegion.add(address); } return; } if (defaultIsolationAddress.remove(address)) { LOGGER.warn("restore default address [{}]", address); addresses.add(address); } } public void resetFailureStatus(String address) { addressFailureStatus.put(address, 0); } public void recordFailState(String address) { synchronized (lock) { if (!addressFailureStatus.containsKey(address)) { addressFailureStatus.put(address, 1); return; } int number = addressFailureStatus.get(address) + 1; if (number < ISOLATION_THRESHOLD) { addressFailureStatus.put(address, number); } else { removeAddress(address); } } } //Query whether the current address belongs to the same AZ or the same region through AZMap, // and delete it from the record. At the same time, add records in history and cache @VisibleForTesting void removeAddress(String address) { if (!addressAutoRefreshed) { if (addresses.remove(address)) { LOGGER.warn("isolation default address [{}]", address); defaultIsolationAddress.add(address); } return; } if (availableZone.remove(address)) { LOGGER.warn("isolation same zone address [{}]", address); isolationZoneAddress.add(address); if (eventBus != null && availableZone.isEmpty() && !availableRegion.isEmpty()) { eventBus.post(new EngineConnectChangedEvent()); } return; } if (availableRegion.remove(address)) { LOGGER.warn("isolation same region address [{}]", address); isolationRegionAddress.add(address); } } public void setEventBus(EventBus eventBus) { this.eventBus = eventBus; } public List getIsolationAddresses() { List isolationAddresses = new ArrayList<>(defaultIsolationAddress); isolationAddresses.addAll(isolationZoneAddress); isolationAddresses.addAll(isolationRegionAddress); return isolationAddresses; } public String compareAndGetAddress(String host) { for (String address : defaultAddress) { if (isAddressHostSame(address, host)) { return address; } } return ""; } private boolean isAddressHostSame(String address, String host) { if (StringUtils.isEmpty(host)) { return false; } try { URI uri = new URI(address); return host.equals(uri.getHost()); } catch (Exception e) { LOGGER.warn("Exception occurred while constructing URI using the address [{}]", address); } return false; } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; public interface HttpConfiguration { class SSLProperties { private boolean enabled; private SSLOption sslOption; private SSLCustom sslCustom; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public SSLOption getSslOption() { return sslOption; } public void setSslOption(SSLOption sslOption) { this.sslOption = sslOption; } public SSLCustom getSslCustom() { return sslCustom; } public void setSslCustom(SSLCustom sslCustom) { this.sslCustom = sslCustom; } } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.StringEntity; public class HttpRequest { public static final String GET = "GET"; public static final String POST = "POST"; public static final String DELETE = "DELETE"; public static final String PUT = "PUT"; private String method; private final String url; private Map headers; private final String content; public HttpRequest(String url, Map headers, String content, String method) { this.url = url; this.headers = headers; this.content = content; this.method = method; } public String getUrl() { return url; } public Map getHeaders() { return headers; } public void addHeader(String name, String value) { if (headers == null) { headers = new HashMap<>(); } headers.put(name, value); } public String getContent() { return content; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public HttpUriRequest getRealRequest() { HttpUriRequest httpUriRequest = null; switch (method) { case GET: { httpUriRequest = new HttpGet(url); break; } case POST: { httpUriRequest = new HttpPost(url); if (content != null) { ((HttpPost) httpUriRequest).setEntity(new StringEntity(content, "UTF-8")); } break; } case DELETE: { httpUriRequest = new HttpDelete(url); break; } case PUT: { httpUriRequest = new HttpPut(url); if (content != null) { ((HttpPut) httpUriRequest).setEntity(new StringEntity(content, "UTF-8")); } break; } default: { httpUriRequest = RequestBuilder.create(method).build(); } } Optional.ofNullable(httpUriRequest).ifPresent(request -> headers.forEach(request::addHeader)); return httpUriRequest; } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import org.apache.http.Header; public class HttpResponse { private int statusCode; private String message; private String content; private Header[] headers; public HttpResponse() { } HttpResponse(int statusCode, String message, String content, Header[] headers) { this.statusCode = statusCode; this.content = content; this.message = message; this.headers = headers; } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getHeader(String key) { if (headers == null) { return null; } for (Header header : headers) { if (header.getName().equals(key)) { return header.getValue(); } } return null; } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.io.IOException; import java.util.Map; /** * Created by on 2019/10/16. */ public interface HttpTransport { HttpResponse get(HttpRequest request) throws IOException; HttpResponse post(HttpRequest request) throws IOException; HttpResponse put(HttpRequest request) throws IOException; HttpResponse delete(HttpRequest request) throws IOException; HttpResponse doRequest(HttpRequest request) throws IOException; void addHeaders(Map headers); } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.servicecomb.foundation.ssl.SSLManager; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; public class HttpTransportFactory { // All parameters set to 5 seconds now. public static final int CONNECT_TIMEOUT = 5000; public static final int CONNECTION_REQUEST_TIMEOUT = 5000; public static final int SOCKET_TIMEOUT = 5000; public static final int MAX_TOTAL = 100; public static final int DEFAULT_MAX_PER_ROUTE = 10; private HttpTransportFactory() { } public static HttpTransport createHttpTransport(HttpConfiguration.SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider, HttpClientBuilder httpClientBuilder) { PoolingHttpClientConnectionManager connectionManager = getPoolingHttpClientConnectionManager(sslProperties); httpClientBuilder.setConnectionManager(connectionManager).disableCookieManagement(); return new HttpTransportImpl(httpClientBuilder.build(), requestAuthHeaderProvider); } public static HttpTransport createHttpTransport(HttpConfiguration.SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider, RequestConfig config) { PoolingHttpClientConnectionManager connectionManager = getPoolingHttpClientConnectionManager(sslProperties); HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(config). setConnectionManager(connectionManager). disableCookieManagement(); return new HttpTransportImpl(httpClientBuilder.build(), requestAuthHeaderProvider); } private static PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager(SSLProperties sslProperties) { //register http/https socket factory RegistryBuilder builder = RegistryBuilder.create(); builder.register("http", PlainConnectionSocketFactory.INSTANCE); if (sslProperties.isEnabled()) { builder.register("https", new SSLConnectionSocketFactory( SSLManager.createSSLContext(sslProperties.getSslOption(), sslProperties.getSslCustom()), NoopHostnameVerifier.INSTANCE)); } Registry connectionSocketFactoryRegistry = builder.build(); //connection pool management PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager( connectionSocketFactoryRegistry); connectionManager.setMaxTotal(MAX_TOTAL); connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE); return connectionManager; } public static HttpTransport createHttpTransport(HttpConfiguration.SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider) { return createHttpTransport(sslProperties, requestAuthHeaderProvider, defaultRequestConfig().build()); } public static RequestConfig.Builder defaultRequestConfig() { return RequestConfig.custom() .setConnectTimeout(CONNECT_TIMEOUT) .setConnectionRequestTimeout( CONNECTION_REQUEST_TIMEOUT) .setSocketTimeout(SOCKET_TIMEOUT); } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.io.IOException; import java.net.URI; import java.util.Map; import org.apache.http.client.HttpClient; import org.apache.http.util.EntityUtils; import org.apache.servicecomb.foundation.auth.SignRequest; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Created by on 2019/10/16. */ public class HttpTransportImpl implements HttpTransport { private static final Logger LOGGER = LoggerFactory.getLogger(HttpTransportImpl.class); private static final String HEADER_CONTENT_TYPE = "Content-Type"; private static final String HEADER_USER_AGENT = "User-Agent"; private HttpClient httpClient; private Map globalHeaders; private final RequestAuthHeaderProvider requestAuthHeaderProvider; public HttpTransportImpl(HttpClient httpClient, RequestAuthHeaderProvider requestAuthHeaderProvider) { this.httpClient = httpClient; this.requestAuthHeaderProvider = requestAuthHeaderProvider; } public HttpClient getHttpClient() { return httpClient; } // for testing. void setHttpClient(HttpClient httpClient) { this.httpClient = httpClient; } @Override public HttpResponse get(HttpRequest request) throws IOException { request.setMethod(HttpRequest.GET); return doRequest(request); } @Override public HttpResponse post(HttpRequest request) throws IOException { request.setMethod(HttpRequest.POST); return doRequest(request); } @Override public HttpResponse put(HttpRequest request) throws IOException { request.setMethod(HttpRequest.PUT); return doRequest(request); } @Override public HttpResponse delete(HttpRequest request) throws IOException { request.setMethod(HttpRequest.DELETE); return doRequest(request); } public HttpResponse doRequest(HttpRequest httpRequest) throws IOException { //add header httpRequest.addHeader(HEADER_CONTENT_TYPE, "application/json"); httpRequest.addHeader(HEADER_USER_AGENT, "microservice-client/1.0.0"); if (globalHeaders != null) { globalHeaders.forEach(httpRequest::addHeader); } httpRequest.getHeaders().putAll(requestAuthHeaderProvider.loadAuthHeader(createSignRequest(httpRequest.getUrl()))); //get Http response org.apache.http.HttpResponse response = httpClient.execute(httpRequest.getRealRequest()); return new HttpResponse(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), response.getEntity() == null ? null : EntityUtils.toString(response.getEntity(), "UTF-8"), response.getAllHeaders()); } private static SignRequest createSignRequest(String url) { try { URI uri = URI.create(url); SignRequest signRequest = new SignRequest(); signRequest.setEndpoint(uri); return signRequest; } catch (Exception e) { LOGGER.error("create signRequest failed!", e); return null; } } @Override public void addHeaders(Map headers) { this.globalHeaders = headers; } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.io.IOException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; public final class HttpUtils { private static final String ALGORITHM_HMACSHA256 = "HmacSHA256"; private static final ObjectMapper MAPPER = new MessageObjectMapper(); public static T deserialize(String content, Class clazz) throws IOException { return MAPPER.readValue(content, clazz); } public static T deserialize(String content, TypeReference clazz) throws IOException { return MAPPER.readValue(content, clazz); } public static String serialize(Object value) throws IOException { return MAPPER.writeValueAsString(value); } public static JsonNode readTree(String content) throws IOException { return MAPPER.readTree(content); } public static String encodeURLParam(String value) throws IOException { if (value == null) { return ""; } return URLEncoder.encode(value, StandardCharsets.UTF_8); } public static String decodeURLParam(String value) throws IOException { if (value == null) { return null; } return URLDecoder.decode(value, StandardCharsets.UTF_8); } public static String sha256Encode(String key, String data) throws Exception { Mac mac = Mac.getInstance(ALGORITHM_HMACSHA256); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM_HMACSHA256); mac.init(secretKey); return Hex.encodeHexString(mac.doFinal(data.getBytes(StandardCharsets.UTF_8))); } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/MessageObjectMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import com.fasterxml.jackson.core.JsonParser.Feature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class MessageObjectMapper extends ObjectMapper { private static final long serialVersionUID = 189026839992490564L; public MessageObjectMapper() { getFactory().disable(Feature.AUTO_CLOSE_SOURCE); // Enable features that can tolerance errors and not enable those make more constraints for compatible reasons. // Developers can use validation api to do more checks. disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // no view annotations shouldn't be included in JSON this._deserializationConfig = this._deserializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); this._serializationConfig = this._serializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/SSLSocketFactoryExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import javax.net.ssl.SSLSocketFactory; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; public class SSLSocketFactoryExt extends SSLSocketFactory { private final SSLSocketFactory sslSocketFactory; private final String host; private final int port; public SSLSocketFactoryExt(SSLSocketFactory factory, String host, int port) { this.sslSocketFactory = factory; this.host = host; this.port = port; } @Override public String[] getDefaultCipherSuites() { return this.sslSocketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return this.sslSocketFactory.getSupportedCipherSuites(); } @Override public Socket createSocket() throws IOException { return this.sslSocketFactory.createSocket(host, port); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { return this.sslSocketFactory.createSocket(socket, host, port, autoClose); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return this.sslSocketFactory.createSocket(host, port); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return this.sslSocketFactory.createSocket(host, port, localHost, localPort); } @Override public Socket createSocket(InetAddress localHost, int port) throws IOException { return this.sslSocketFactory.createSocket(localHost, port); } @Override public Socket createSocket(InetAddress localHost, int localPort, InetAddress localHost1, int localPort1) throws IOException { return this.sslSocketFactory.createSocket(localHost, localPort, localHost1, localPort1); } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; public class URLEndPoint { private static final String SSL_ENABLED_KEY = "sslEnabled"; private static final String HTTP_KEY = "http://"; private static final String HTTPS_KEY = "https://"; private final boolean sslEnabled; private final Map> queries; private final String hostOrIp; private final int port; public URLEndPoint(String endpoint) { URI uri = URI.create(endpoint); hostOrIp = uri.getHost(); if (uri.getPort() < 0) { throw new IllegalArgumentException("port not specified."); } port = uri.getPort(); queries = splitQuery(uri); if (endpoint.contains(HTTPS_KEY)) { sslEnabled = true; } else { sslEnabled = Boolean.parseBoolean(getFirst(SSL_ENABLED_KEY)); } } public static Map> splitQuery(URI uri) { final Map> queryPairs = new LinkedHashMap<>(); List pairs = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8); for (NameValuePair pair : pairs) { List list = queryPairs.computeIfAbsent(pair.getName(), name -> new ArrayList<>()); list.add(pair.getValue()); } return queryPairs; } public String getFirst(String key) { List values = queries.get(key); if (values == null) { return null; } return values.get(0); } @Override public String toString() { if (sslEnabled) { return HTTPS_KEY + hostOrIp + ":" + port; } return HTTP_KEY + hostOrIp + ":" + port; } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import org.java_websocket.handshake.ServerHandshake; public interface WebSocketListener { void onMessage(String s); void onError(Exception e); void onClose(int code, String reason, boolean remote); void onOpen(ServerHandshake serverHandshake); } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; import javax.net.ssl.SSLSocketFactory; import org.apache.servicecomb.foundation.ssl.SSLManager; import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft_6455; import org.java_websocket.handshake.ServerHandshake; public class WebSocketTransport extends WebSocketClient { public static final int CONNECT_TIMEOUT = 5000; private final WebSocketListener webSocketListener; public WebSocketTransport(String serverUri, HttpConfiguration.SSLProperties sslProperties, Map headers, WebSocketListener webSocketListener) throws URISyntaxException { super(new URI(serverUri), new Draft_6455(), headers, CONNECT_TIMEOUT); if (sslProperties.isEnabled()) { SSLSocketFactory sslSocketFactory = SSLManager .createSSLSocketFactory(sslProperties.getSslOption(), sslProperties.getSslCustom()); URI uri = new URI(serverUri); setSocketFactory(new SSLSocketFactoryExt(sslSocketFactory, uri.getHost(), uri.getPort())); } this.webSocketListener = webSocketListener; } @Override public void onOpen(ServerHandshake serverHandshake) { this.webSocketListener.onOpen(serverHandshake); } @Override public void onMessage(String s) { this.webSocketListener.onMessage(s); } @Override public void onClose(int code, String reason, boolean remote) { this.webSocketListener.onClose(code, reason, remote); } @Override public void onError(Exception e) { this.webSocketListener.onError(e); } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.event; public class EngineConnectChangedEvent { } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.event; public abstract class OperationEvents { public static class UnAuthorizedOperationEvent extends OperationEvents { private final String address; public UnAuthorizedOperationEvent(String address) { this.address = address; } public String getAddress() { return address; } } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/RefreshEndpointEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.event; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class RefreshEndpointEvent { public static final String SERVICE_CENTER_NAME = "SERVICECENTER"; public static final String KIE_NAME = "KIE"; public static final String CONFIG_CENTER_NAME = "CseConfigCenter"; public static final String CSE_MONITORING_NAME = "CseMonitoring"; private static final String SAME_ZONE = "sameZone"; private static final String SAME_REGION = "sameRegion"; private Map> zoneAndRegion = new HashMap<>(); private String name; public RefreshEndpointEvent(Map> zoneAndRegion, String name) { this.zoneAndRegion = zoneAndRegion; this.name = name; } public List getSameZone() { if (zoneAndRegion.get(SAME_ZONE).isEmpty()) { return new ArrayList<>(); } return zoneAndRegion.get(SAME_ZONE); } public List getSameRegion() { if (zoneAndRegion.get(SAME_REGION).isEmpty()) { return new ArrayList<>(); } return zoneAndRegion.get(SAME_REGION); } public Map> getZoneAndRegion() { return zoneAndRegion; } public void setZoneAndRegion(Map> zoneAndRegion) { this.zoneAndRegion = zoneAndRegion; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.task; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AbstractTask { public class BackOffSleepTask implements Task { private static final long BASE = 3000; private static final long MAX = 10 * 60 * 1000; long waitTime; Task nextTask; public BackOffSleepTask(int failedCount, Task nextTask) { this.waitTime = failedCount * failedCount * BASE; this.nextTask = nextTask; } public BackOffSleepTask(long waitTime, Task nextTask) { this.waitTime = waitTime; this.nextTask = nextTask; } @Override public void execute() { long time = Math.min(MAX, waitTime); try { Thread.sleep(time); } catch (InterruptedException e) { LOGGER.error("unexpected interrupt during sleep", e); } startTask(nextTask); } } private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTask.class); protected ExecutorService taskPool; private volatile boolean running = true; public static AtomicInteger taskCounter = new AtomicInteger(0); private ScheduledExecutorService addrCheckExecutor; protected AbstractTask(String taskName) { initTaskPool(taskName); Runtime.getRuntime().addShutdownHook(new Thread(AbstractTask.this::stop, taskName + "-shutdown-hook")); } protected void initTaskPool(String taskName) { this.taskPool = Executors.newSingleThreadExecutor((task) -> new Thread(task, taskName + "-" + taskCounter.getAndIncrement())); } protected void schedulerCheckAddressAvailable(String taskName, Runnable task, long delayTime) { if (addrCheckExecutor == null) { addrCheckExecutor = Executors.newScheduledThreadPool(1, (t) -> new Thread(t, taskName)); } addrCheckExecutor.scheduleWithFixedDelay(task, delayTime, delayTime, TimeUnit.MILLISECONDS); } protected void startTask(Task task) { if (!running) { return; } try { this.taskPool.execute(() -> { try { task.execute(); } catch (Throwable e) { LOGGER.error("unexpected error execute task {}", task.getClass().getName(), e); } }); } catch (RejectedExecutionException e) { LOGGER.error("execute task rejected {}", task.getClass().getName(), e); } } public void stop() { try { running = false; this.taskPool.shutdown(); this.taskPool.awaitTermination(10, TimeUnit.SECONDS); if (addrCheckExecutor != null) { this.addrCheckExecutor.shutdown(); this.addrCheckExecutor.awaitTermination(10, TimeUnit.SECONDS); } } catch (InterruptedException e) { LOGGER.warn("tasks not shutdown in time {}", e.getMessage()); } } } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/Task.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.task; public interface Task { void execute(); } ================================================ FILE: clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.utils; import org.apache.http.HttpStatus; import org.apache.servicecomb.http.client.common.AbstractAddressManager; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; public class ServiceCombServiceAvailableUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCombServiceAvailableUtils.class); public static void checkAddressAvailable(AbstractAddressManager addressManager, String address, HttpTransport httpTransport, String path) { String formatUrl = addressManager.formatUrl(path, true, address); HttpRequest httpRequest = new HttpRequest(formatUrl, null, null, HttpRequest.GET); try { HttpResponse response = httpTransport.doRequest(httpRequest); if (response.getStatusCode() == HttpStatus.SC_OK) { addressManager.recordSuccessState(address); return; } // old server does not provide the check api, using TCP checks whether the server is ready. if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND && telnetCheckAddress(address)) { LOGGER.warn("[{}] path does not provide, tcp check address ready!", path); addressManager.recordSuccessState(address); } } catch (IOException e) { LOGGER.error("check isolation address [{}] available error!", address); } } private static boolean telnetCheckAddress(String address) { URI ipPort = parseIpPortFromURI(address); if (ipPort == null) { return false; } try (Socket s = new Socket()) { s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000); return true; } catch (IOException e) { LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address); } return false; } private static URI parseIpPortFromURI(String address) { try { return new URI(address); } catch (URISyntaxException e) { LOGGER.error("build uri error with address [{}].", address); return null; } } } ================================================ FILE: clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class AbstractAddressManagerTest { private static final List addresses = new ArrayList<>(); private static AbstractAddressManager addressManager1; private static AbstractAddressManager addressManager2; private static AbstractAddressManager addressManager3; @BeforeEach public void setUp() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); addressManager1 = new AbstractAddressManager(addresses, "", ""); addressManager2 = new AbstractAddressManager("project", addresses, "", ""); addressManager3 = new AbstractAddressManager(null, addresses, "", ""); Field addressManagerField = addressManager1.getClass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); addressManagerField = addressManager2.getClass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager2, 0); addressManagerField = addressManager3.getClass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager3, 0); } @AfterEach public void tearDown() { addresses.clear(); addressManager1 = null; addressManager2 = null; addressManager3 = null; } @Test public void abstractAddressManagerTest() { Assertions.assertNotNull(addressManager1); Assertions.assertNotNull(addressManager2); Assertions.assertNotNull(addressManager3); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); } @Test public void recordStateTest() throws ExecutionException { List addressAZ = new ArrayList<>(); addressAZ.add("http://127.0.0.3:30100"); List addressRG = new ArrayList<>(); addressRG.add("http://127.0.0.4:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", "") {}; addressManager.refreshEndpoint(event, "TEST"); String address = "http://127.0.0.3:30100"; addressManager.recordFailState(address); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); addressManager.recordFailState(address); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); // test fail 2 times ,it will not be isolated addressManager.resetFailureStatus(address); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); // test recodeStatus times Map recodeStatus = addressManager.getAddressFailureStatus(); Assertions.assertEquals(0, (int) recodeStatus.get("http://127.0.0.3:30100")); // test fail 3 times ,it will be isolated addressManager.recordFailState(address); addressManager.recordFailState(address); addressManager.recordFailState(address); Assertions.assertEquals("http://127.0.0.4:30100", addressManager.address()); // test restore isolation addressManager.recordSuccessState("http://127.0.0.3:30100"); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); } @Test public void testMultipleThread() throws Exception { AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", ""); String address = "http://127.0.0.3:30100"; CountDownLatch latch = new CountDownLatch(2); for (int i = 0; i < 2; i++) { new Thread(() -> { addressManager.recordFailState(address); latch.countDown(); }).start(); } latch.await(30, TimeUnit.SECONDS); Map recodeStatus = addressManager.getAddressFailureStatus(); Assertions.assertEquals(2, (int) recodeStatus.get("http://127.0.0.3:30100")); } @Test public void addressForOnlyDefaultTest() { Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); Assertions.assertEquals("https://127.0.0.2:30103/v3/project", addressManager2.address()); Assertions.assertEquals("http://127.0.0.1:30103/v3/project", addressManager2.address()); Assertions.assertEquals("https://127.0.0.2:30103/v3/default", addressManager3.address()); Assertions.assertEquals("http://127.0.0.1:30103/v3/default", addressManager3.address()); } @Test public void addressForOnlyAzTest() { List addressAZ = new ArrayList<>(); addressAZ.add("http://127.0.0.1:30100"); addressAZ.add("https://127.0.0.2:30100"); addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true"); addressAZ.add("rest://127.0.0.2:30100?sslEnabled=false"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", new ArrayList<>()); RefreshEndpointEvent event1 = new RefreshEndpointEvent(zoneAndRegion, "TEST"); addressManager1.refreshEndpoint(event1, "TEST"); Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.1:30100", addressManager1.address()); Assertions.assertEquals("http://127.0.0.2:30100", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); } @Test public void addressForOnlyRegionTest() { List addressRG = new ArrayList<>(); addressRG.add("rest://127.0.0.5:30100?sslEnabled=true"); addressRG.add("rest://127.0.0.6:30100"); addressRG.add("http://127.0.0.7:30100"); addressRG.add("https://127.0.0.8:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", new ArrayList<>()); zoneAndRegion.put("sameRegion", addressRG); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); addressManager1.refreshEndpoint(event, "TEST"); Assertions.assertEquals("http://127.0.0.6:30100", addressManager1.address()); Assertions.assertEquals("http://127.0.0.7:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.8:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.5:30100", addressManager1.address()); Assertions.assertEquals("http://127.0.0.6:30100", addressManager1.address()); } @Test public void addressForAzAndRegionTest() { List addressAZ = new ArrayList<>(); addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true"); addressAZ.add("https://127.0.0.2:30100"); List addressRG = new ArrayList<>(); addressRG.add("rest://127.0.0.3:30100?sslEnabled=true"); addressRG.add("https://127.0.0.4:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); addressManager1.refreshEndpoint(event, "TEST"); Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.1:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); addressManager1.removeAddress("https://127.0.0.2:30100"); addressManager1.removeAddress("https://127.0.0.1:30100"); Assertions.assertEquals("https://127.0.0.3:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.4:30100", addressManager1.address()); Assertions.assertEquals("https://127.0.0.3:30100", addressManager1.address()); addressManager1.removeAddress("https://127.0.0.4:30100"); addressManager1.removeAddress("https://127.0.0.3:30100"); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); } @Test public void sslEnabledTest() { Assertions.assertTrue(addressManager1.sslEnabled()); Assertions.assertFalse(addressManager1.sslEnabled()); Assertions.assertTrue(addressManager1.sslEnabled()); Assertions.assertTrue(addressManager2.sslEnabled()); Assertions.assertFalse(addressManager2.sslEnabled()); Assertions.assertTrue(addressManager2.sslEnabled()); } @Test public void transformAddressTest() { List address = new ArrayList<>(); address.add("rest://127.0.0.1:30100?sslEnabled=true"); address.add("rest://127.0.0.2:30100"); address.add("http://127.0.0.3:30100"); address.add("https://127.0.0.4:30100"); List formAddress = addressManager2.transformAddress(address); Assertions.assertEquals("rest://127.0.0.1:30100?sslEnabled=true/v3/project", formAddress.get(0)); Assertions.assertEquals("rest://127.0.0.2:30100/v3/project", formAddress.get(1)); Assertions.assertEquals("http://127.0.0.3:30100/v3/project", formAddress.get(2)); Assertions.assertEquals("https://127.0.0.4:30100/v3/project", formAddress.get(3)); formAddress = addressManager3.transformAddress(address); Assertions.assertEquals("rest://127.0.0.1:30100?sslEnabled=true/v3/default", formAddress.get(0)); Assertions.assertEquals("rest://127.0.0.2:30100/v3/default", formAddress.get(1)); Assertions.assertEquals("http://127.0.0.3:30100/v3/default", formAddress.get(2)); Assertions.assertEquals("https://127.0.0.4:30100/v3/default", formAddress.get(3)); } @Test public void getUrlPrefixTest() { Assertions.assertEquals("http://127.0.0.3:30100/v3/", addressManager2.getUrlPrefix("http://127.0.0.3:30100")); Assertions.assertEquals("http://127.0.0.3:30100/v3/", addressManager3.getUrlPrefix("http://127.0.0.3:30100")); } @Test public void refreshEndpointTest() { List addressAZ = new ArrayList<>(); addressAZ.add("rest://127.0.0.1:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", new ArrayList<>()); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); addressManager1.refreshEndpoint(event, "KIE"); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); addressManager2.refreshEndpoint(event, "TEST"); Assertions.assertEquals("http://127.0.0.1:30100", addressManager2.address()); Assertions.assertEquals("http://127.0.0.1:30100", addressManager2.address()); } @Test public void normalizeIPV4Test() { String uri = addressManager1.normalizeUri("rest://127.0.0.1:30100?sslEnabled=true"); Assertions.assertEquals("https://127.0.0.1:30100", uri); uri = addressManager1.normalizeUri("rest://127.0.0.1:30100?sslEnabled=false"); Assertions.assertEquals("http://127.0.0.1:30100", uri); uri = addressManager1.normalizeUri("rest://127.0.0.1:30100"); Assertions.assertEquals("http://127.0.0.1:30100", uri); } @Test public void normalizeIPV6Test() { String uri = addressManager1.normalizeUri("rest://[2008::7:957f:b2d6:1af4:a1f8]:30100?sslEnabled=true"); Assertions.assertEquals("https://[2008::7:957f:b2d6:1af4:a1f8]:30100", uri); uri = addressManager1.normalizeUri("rest://[2008::7:957f:b2d6:1af4:a1f8]:30100"); Assertions.assertEquals("http://[2008::7:957f:b2d6:1af4:a1f8]:30100", uri); } @Test public void compareAndGetAddressTest() { List testAddr = new ArrayList<>(); testAddr.add("https://192.168.20.160:30100"); testAddr.add("https://127.0.0.1:30100"); testAddr.add("https://127.0.0.3:30100"); AbstractAddressManager manager = new AbstractAddressManager(testAddr, "", ""); Assertions.assertTrue(manager.compareAndGetAddress("192.168.20.16").isEmpty()); Assertions.assertEquals("https://192.168.20.160:30100", manager.compareAndGetAddress("192.168.20.160")); } @Test public void AddressAffinityTest() { List testAddr = new ArrayList<>(); testAddr.add("https://192.168.20.160:30100?region=region1&availableZone=zone1"); testAddr.add("https://127.0.0.1:30100"); AbstractAddressManager manager = new AbstractAddressManager(testAddr, "region1", "zone1"); Assertions.assertEquals("https://192.168.20.160:30100", manager.address()); AbstractAddressManager manager2 = new AbstractAddressManager("default", testAddr, "region1", "zone1"); Assertions.assertEquals("https://192.168.20.160:30100", manager2.address()); } } ================================================ FILE: clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/HttpTransportImplTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.http.client.common; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpVersion; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class HttpTransportImplTest { @Test public void TestHttpTransport() throws IOException { HttpClient httpClient = mock(HttpClient.class); RequestAuthHeaderProvider requestAuthHeaderProvider = mock(RequestAuthHeaderProvider.class); org.apache.http.HttpResponse httpResponse = mock(org.apache.http.HttpResponse.class); StatusLine statusLine = mock(StatusLine.class); when(statusLine.getStatusCode()).thenReturn(200); when(statusLine.getProtocolVersion()).thenReturn(HttpVersion.HTTP_1_1); when(statusLine.getReasonPhrase()).thenReturn("OK"); when(httpResponse.getStatusLine()).thenReturn(statusLine); when(httpResponse.getEntity()).thenReturn(new StringEntity("Test", ContentType.APPLICATION_JSON)); when(httpClient.execute(Mockito.any())).thenReturn(httpResponse); HttpTransportImpl httpTransport = new HttpTransportImpl(httpClient, requestAuthHeaderProvider); Map extraHeaders = new HashMap<>(); extraHeaders.put("test", "testContext"); httpTransport.addHeaders(extraHeaders); HttpRequest httpRequest = new HttpRequest("111", null, null, HttpRequest.GET); HttpResponse actualResponse = httpTransport.get(httpRequest); Assertions.assertNotNull(actualResponse); Assertions.assertEquals(200, actualResponse.getStatusCode()); Assertions.assertEquals("OK", actualResponse.getMessage()); Assertions.assertEquals("Test", actualResponse.getContent()); } } ================================================ FILE: clients/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 clients ServiceComb::Client pom http-client-common service-center-client config-common config-kie-client config-center-client dashboard-client ================================================ FILE: clients/service-center-client/README.md ================================================ ## ServiceComb-Service-Center Client for Java Sample Java client for ServiceComb-Service-Center HTTP API. If you want more information about the ServiceComb-Service-Center HTTP API, go [here](https://github.com/apache/servicecomb-service-center/blob/master/docs/openapi/v4.yaml). ### Build & Install local Build from source : ``` maven clean install ``` add dependency to maven ``` org.apache.servicecomb service-center-client ``` ### Basic Usage #### Case 1: Health check ```Java ServiceCenterClient client = new ServiceCenterClient(); //get service-center instance MicroserviceInstancesResponse instances = client.getServiceCenterInstances(); ``` #### Case 2: Register microservice ```Java ServiceCenterClient client = new ServiceCenterClient(); // state Microservice object, and you have to set a serviceName Microservice microservice = new Microservice(); microservice.setServiceName("Test"); microservice.setServiceId("111111"); //register microservice String response = client.registerMicroservice(microservice); ``` #### Case 3: Get microservice list ```Java ServiceCenterClient client = new ServiceCenterClient(); //get services lists MicroservicesResponse services = client.getMicroserviceList(); ``` #### Case 4: Register service instance ```Java ServiceCenterClient client = new ServiceCenterClient(); //state MicroserviceInstance object, you have to set existed serviceId MicroserviceInstance instance = new MicroserviceInstance(); instance.setServiceId("222222"); //register service instanceId, return instanceId String instanceId = client.registerMicroserviceInstance(instance, "222222"); ``` #### Case 5: Get service instances list ```Java ServiceCenterClient client = new ServiceCenterClient(); // get instances list with servcieId being "222222" MicroserviceInstancesResponse instances = client.getMicroserviceInstanceList("222222"); ``` #### Case 6: Heartbeats ```Java ServiceCenterClient client = new ServiceCenterClient(); //all services and all instances send heartbeats MicroservicesResponse services = client.getMicroserviceList(); for(Microservice microservice : services.getServices()) { for (MicroserviceInstance instance: client.getMicroserviceInstanceList(microservice.getServiceId()).getInstances()) { client.sendHeartBeats(new HeartbeatsRequest(microservice.getServiceId(),instance.getInstanceId())); } } ``` #### Other API You can see client API code and tests, go [here](https://github.com/apache/servicecomb-java-chassis/blob/master/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java) ### More development - Support All Service-center HTTP API ### Contact Bugs/Feature : [issues](https://github.com/apache/servicecomb-java-chassis/issues) ================================================ FILE: clients/service-center-client/pom.xml ================================================ clients org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 service-center-client ServiceComb::Clients::Service Center Client org.apache.servicecomb http-client-common com.google.guava failureaccess test ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/CacheableServiceCenterOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; public interface CacheableServiceCenterOperation { } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/DiscoveryEvents.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.util.List; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; public abstract class DiscoveryEvents { public static class InstanceChangedEvent extends DiscoveryEvents { private final String appName; private final String serviceName; private final List instances; public InstanceChangedEvent(String appName, String serviceName, List instances) { this.appName = appName; this.serviceName = serviceName; this.instances = instances; } public String getAppName() { return appName; } public String getServiceName() { return serviceName; } public List getInstances() { return instances; } } /** * internal events to ask for a immediate instance pull */ public static class PullInstanceEvent extends DiscoveryEvents { } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/RegistrationEvents.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; public abstract class RegistrationEvents { protected final boolean success; protected final Microservice microservice; protected RegistrationEvents(boolean success, Microservice microservice) { this.success = success; this.microservice = microservice; } public boolean isSuccess() { return this.success; } public Microservice getMicroservice() { return microservice; } public static class MicroserviceRegistrationEvent extends RegistrationEvents { public MicroserviceRegistrationEvent(boolean success, Microservice microservice) { super(success, microservice); } } public static class SchemaRegistrationEvent extends RegistrationEvents { public SchemaRegistrationEvent(boolean success, Microservice microservice) { super(success, microservice); } } public static class MicroserviceInstanceRegistrationEvent extends RegistrationEvents { protected final MicroserviceInstance microserviceInstance; public MicroserviceInstanceRegistrationEvent(boolean success, Microservice microservice, MicroserviceInstance microserviceInstance) { super(success, microservice); this.microserviceInstance = microserviceInstance; } } public static class HeartBeatEvent extends RegistrationEvents { protected final MicroserviceInstance microserviceInstance; public HeartBeatEvent(boolean success, Microservice microservice, MicroserviceInstance microserviceInstance) { super(success, microservice); this.microserviceInstance = microserviceInstance; } } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.util.List; import org.apache.servicecomb.http.client.common.AbstractAddressManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ServiceCenterAddressManager extends AbstractAddressManager { public ServiceCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, String availableZone) { super(projectName, addresses, region, availableZone); eventBus.register(this); } @Override protected List transformAddress(List addresses) { return addresses; } @Override protected String getUrlPrefix(String address) { return address + V4_PREFIX; } @Subscribe public void onRefreshEndpointEvent(RefreshEndpointEvent event) { refreshEndpoint(event, RefreshEndpointEvent.SERVICE_CENTER_NAME); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.io.IOException; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.utils.URIBuilder; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.http.client.common.HttpUtils; import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.CreateMicroserviceInstanceRequest; import org.apache.servicecomb.service.center.client.model.CreateMicroserviceRequest; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.ErrorMessage; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.GetSchemaListResponse; import org.apache.servicecomb.service.center.client.model.GetSchemaResponse; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceResponse; import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; import org.apache.servicecomb.service.center.client.model.MicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.MicroserviceResponse; import org.apache.servicecomb.service.center.client.model.MicroservicesResponse; import org.apache.servicecomb.service.center.client.model.ModifySchemasRequest; import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.apache.servicecomb.service.center.client.model.UpdatePropertiesRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; public class ServiceCenterClient implements ServiceCenterOperation { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterClient.class); private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.registry.sc.client.timeout.connect"; private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.registry.sc.client.timeout.request"; private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.registry.sc.client.timeout.socket"; private final ServiceCenterRawClient httpClient; private final ServiceCenterAddressManager addressManager; public ServiceCenterClient(ServiceCenterRawClient httpClient, ServiceCenterAddressManager addressManager) { this.httpClient = httpClient; this.addressManager = addressManager; } public ServiceCenterClient setEventBus(EventBus eventBus) { addressManager.setEventBus(eventBus); this.httpClient.setEventBus(eventBus); return this; } public ServiceCenterClient(ServiceCenterAddressManager addressManager, SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider, String tenantName, Map extraGlobalHeaders, Environment environment) { HttpTransport httpTransport = HttpTransportFactory.createHttpTransport(sslProperties, requestAuthHeaderProvider, buildRequestConfig(environment)); httpTransport.addHeaders(extraGlobalHeaders); this.httpClient = new ServiceCenterRawClient.Builder() .setTenantName(tenantName) .setAddressManager(addressManager) .setHttpTransport(httpTransport).build(); this.addressManager = addressManager; } private RequestConfig buildRequestConfig(Environment environment) { RequestConfig.Builder builder = HttpTransportFactory.defaultRequestConfig(); if (environment == null) { return builder.build(); } builder.setConnectTimeout(environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000)); builder.setConnectionRequestTimeout(environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000)); builder.setSocketTimeout(environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000)); return builder.build(); } @Override public MicroserviceInstancesResponse getServiceCenterInstances() { try { HttpResponse response = httpClient.getHttpRequest("/registry/health", null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); } throw new OperationException( "get service-center instances fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service-center instances fails", e); } } @Override public RegisteredMicroserviceResponse registerMicroservice(Microservice microservice) { try { CreateMicroserviceRequest request = new CreateMicroserviceRequest(); request.setService(microservice); HttpResponse response = httpClient .postHttpRequest("/registry/microservices", null, HttpUtils.serialize(request)); if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); } throw new OperationException( "register service fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "register service fails", e); } } @Override public MicroservicesResponse getMicroserviceList() { try { HttpResponse response = httpClient.getHttpRequest("/registry/microservices", null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroservicesResponse.class); } throw new OperationException( "get service List fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service List fails", e); } } @Override public RegisteredMicroserviceResponse queryServiceId(Microservice microservice) { try { URIBuilder uriBuilder = new URIBuilder("/registry/existence"); uriBuilder.setParameter("type", "microservice"); uriBuilder.setParameter("appId", microservice.getAppId()); uriBuilder.setParameter("serviceName", microservice.getServiceName()); uriBuilder.setParameter("version", microservice.getVersion()); uriBuilder.setParameter("env", microservice.getEnvironment()); HttpResponse response = httpClient.getHttpRequest(uriBuilder.build().toString(), null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); } LOGGER.info("Query serviceId fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); return null; } catch (IOException e) { throw new OperationException( "query serviceId fails", e); } catch (URISyntaxException e) { throw new OperationException( "build url failed.", e); } } @Override public Microservice getMicroserviceByServiceId(String serviceId) { try { HttpResponse response = httpClient.getHttpRequest("/registry/microservices/" + serviceId, null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { MicroserviceResponse microserviceResponse = HttpUtils .deserialize(response.getContent(), MicroserviceResponse.class); return microserviceResponse.getService(); } throw new OperationException( "get service message fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service message fails", e); } } @Override public RegisteredMicroserviceInstanceResponse registerMicroserviceInstance(MicroserviceInstance instance) { try { CreateMicroserviceInstanceRequest request = new CreateMicroserviceInstanceRequest(); request.setInstance(instance); HttpResponse response = httpClient .postHttpRequest("/registry/microservices/" + instance.getServiceId() + "/instances", null, HttpUtils.serialize(request)); if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceInstanceResponse.class); } throw new OperationException( "register service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "register service instance fails", e); } } @Override public FindMicroserviceInstancesResponse findMicroserviceInstance(String consumerId, String appId, String serviceName, String versionRule, String revision) { try { Map headers = new HashMap<>(); headers.put("X-ConsumerId", consumerId); HttpResponse response = httpClient .getHttpRequest("/registry/instances?appId=" + URLEncoder.encode(appId, "UTF-8") + "&serviceName=" + HttpUtils.encodeURLParam(serviceName) + "&version=" + HttpUtils.encodeURLParam(versionRule) + "&rev=" + HttpUtils.encodeURLParam(revision) , headers, null); FindMicroserviceInstancesResponse result = new FindMicroserviceInstancesResponse(); if (response.getStatusCode() == HttpStatus.SC_OK) { result.setModified(true); result.setRevision(response.getHeader("X-Resource-Revision")); result.setMicroserviceInstancesResponse( HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class)); return result; } if (response.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { result.setModified(false); return result; } if (response.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { LOGGER.warn("rate limited, keep the local service {}#{} instance cache unchanged.", appId, serviceName); result.setModified(false); return result; } throw new OperationException( "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service instances list fails", e); } } @Override public MicroserviceInstancesResponse getMicroserviceInstanceList(String serviceId) { try { HttpResponse response = httpClient .getHttpRequest("/registry/microservices/" + serviceId + "/instances", null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); } throw new OperationException( "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service instances list fails", e); } } @Override public MicroserviceInstance getMicroserviceInstance(String serviceId, String instanceId) { try { HttpResponse response = httpClient .getHttpRequest("/registry/microservices/" + serviceId + "/instances/" + instanceId, null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { MicroserviceInstanceResponse instanceResponse = HttpUtils .deserialize(response.getContent(), MicroserviceInstanceResponse.class); return instanceResponse.getInstance(); } throw new OperationException( "get service instance message fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service instance message fails", e); } } @Override public void deleteMicroserviceInstance(String serviceId, String instanceId) { try { HttpResponse response = httpClient .deleteHttpRequest("/registry/microservices/" + serviceId + "/instances/" + instanceId, null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { LOGGER.info("Delete service instance successfully."); return; } throw new OperationException( "delete service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "delete service instance fails", e); } } @Override public boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, MicroserviceInstanceStatus status) { try { HttpResponse response = httpClient.putHttpRequest( "/registry/microservices/" + serviceId + "/instances/" + instanceId + "/status?value=" + status, null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } throw new OperationException( "update service instance status fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "update service instance status fails", e); } } @Override public void sendHeartBeats(HeartbeatsRequest heartbeatsRequest) { try { HttpResponse response = httpClient .putHttpRequest("/registry/heartbeats", null, HttpUtils.serialize(heartbeatsRequest)); if (response.getStatusCode() == HttpStatus.SC_OK) { return; } throw new OperationException( "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "heartbeats fails ", e); } } @Override public boolean sendHeartBeat(String serviceId, String instanceId) { try { HttpResponse response = httpClient .putHttpRequest("/registry/microservices/" + serviceId + "/instances/" + instanceId + "/heartbeat", null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } throw new OperationException( "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "heartbeats fails ", e); } } @Override public List getServiceSchemasList(String serviceId, boolean withContent) { String url = "/registry/microservices/" + serviceId + "/schemas"; if (withContent) { url = url + "?withSchema=1"; } try { HttpResponse response = httpClient .getHttpRequest(url, null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { GetSchemaListResponse getSchemaResponse = HttpUtils .deserialize(response.getContent(), GetSchemaListResponse.class); return getSchemaResponse.getSchemas(); } throw new OperationException( "get service schemas list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service schemas list fails", e); } } /** * Get one schema context of service * * @param serviceId * @param schemaId * @return * @throws OperationException */ public String getServiceSchemaContext(String serviceId, String schemaId) { try { HttpResponse response = httpClient .getHttpRequest("/registry/microservices/" + serviceId + "/schemas/" + schemaId, null, null); if (response.getStatusCode() == HttpStatus.SC_OK) { GetSchemaResponse getSchemaResponse = HttpUtils.deserialize(response.getContent(), GetSchemaResponse.class); return getSchemaResponse.getSchema(); } throw new OperationException( "get service schema context fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "get service schemas context fails", e); } } @Override public boolean registerSchema(String serviceId, String schemaId, CreateSchemaRequest schema) { try { HttpResponse response = httpClient .putHttpRequest("/registry/microservices/" + serviceId + "/schemas/" + schemaId, null, HttpUtils.serialize(schema)); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "update service schema fails", e); } } @Override public boolean updateServiceSchemaContext(String serviceId, SchemaInfo schemaInfo) { try { CreateSchemaRequest request = new CreateSchemaRequest(); request.setSchema(schemaInfo.getSchema()); request.setSummary(schemaInfo.getSummary()); HttpResponse response = httpClient .putHttpRequest("/registry/microservices/" + serviceId + "/schemas/" + schemaInfo.getSchemaId(), null, HttpUtils.serialize(request)); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "update service schema fails", e); } } @Override public boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRequest modifySchemasRequest) { try { HttpResponse response = httpClient .postHttpRequest("/registry/microservices/" + serviceId + "/schemas", null, HttpUtils.serialize(modifySchemasRequest)); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "update service schema fails", e); } } @Override public RbacTokenResponse queryToken(RbacTokenRequest request, String host) { try { String queryAddress = addressManager.compareAndGetAddress(host); HttpResponse response = httpClient .postHttpRequestAbsoluteUrl("/v4/token", null, HttpUtils.serialize(request), queryAddress); if (response.getStatusCode() == HttpStatus.SC_OK) { RbacTokenResponse result = HttpUtils.deserialize(response.getContent(), RbacTokenResponse.class); result.setStatusCode(HttpStatus.SC_OK); return result; } if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) { RbacTokenResponse result = new RbacTokenResponse(); result.setStatusCode(response.getStatusCode()); return result; } if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED || response.getStatusCode() == HttpStatus.SC_FORBIDDEN) { RbacTokenResponse result = new RbacTokenResponse(); result.setStatusCode(response.getStatusCode()); ErrorMessage errorMessage = HttpUtils.deserialize(response.getContent(), ErrorMessage.class); result.setErrorCode(errorMessage.getErrorCode()); return result; } throw new OperationException( "query token failed, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "query token failed", e); } } @Override public boolean updateMicroserviceProperties(String serviceId, Map serviceProperties, Framework framework) { try { UpdatePropertiesRequest request = new UpdatePropertiesRequest(); request.setProperties(serviceProperties); request.setFramework(framework); HttpResponse response = httpClient.putHttpRequest( "/registry/microservices/" + serviceId + "/properties", null, HttpUtils.serialize(request)); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } throw new OperationException( "update service properties fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( "update service properties fails", e); } } @Override public void checkIsolationAddressAvailable() { List isolationAddresses = addressManager.getIsolationAddresses(); if (isolationAddresses.isEmpty()) { return; } for (String address : isolationAddresses) { httpClient.checkAddressAvailable(address); } } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; import org.apache.servicecomb.service.center.client.DiscoveryEvents.InstanceChangedEvent; import org.apache.servicecomb.service.center.client.DiscoveryEvents.PullInstanceEvent; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ServiceCenterDiscovery extends AbstractTask { public static final int MAX_INTERVAL = 600000; public static final int MIN_INTERVAL = 1000; private static final String ALL_VERSION = "0+"; private static volatile boolean pullInstanceTaskOnceInProgress = false; public static class SubscriptionKey { final String appId; final String serviceName; public SubscriptionKey(String appId, String serviceName) { this.appId = appId; this.serviceName = serviceName; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SubscriptionKey that = (SubscriptionKey) o; return appId.equals(that.appId) && serviceName.equals(that.serviceName); } @Override public int hashCode() { return Objects.hash(appId, serviceName); } } public static class SubscriptionValue { String revision; List instancesCache; } private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterDiscovery.class); private final ServiceCenterClient serviceCenterClient; private final EventBus eventBus; private String myselfServiceId; private final Map instancesCache = new ConcurrentHashMap<>(); private final Map microserviceCache = new ConcurrentHashMap<>(); private long pollInterval = 15000; private boolean started = false; private final Object lock = new Object(); private final Random random = new Random(); public ServiceCenterDiscovery(ServiceCenterClient serviceCenterClient, EventBus eventBus) { super("service-center-discovery-task"); this.serviceCenterClient = serviceCenterClient; this.eventBus = eventBus; this.eventBus.register(this); } public ServiceCenterDiscovery setPollInterval(long interval) { if (interval > MAX_INTERVAL || interval < MIN_INTERVAL) { return this; } this.pollInterval = interval; return this; } public void updateMyselfServiceId(String myselfServiceId) { this.myselfServiceId = myselfServiceId; } public void startDiscovery() { if (!started) { started = true; startTask(new PullInstanceTask()); } } public void registerIfNotPresent(SubscriptionKey subscriptionKey) { if (this.instancesCache.get(subscriptionKey) == null) { synchronized (lock) { if (this.instancesCache.get(subscriptionKey) == null) { SubscriptionValue value = new SubscriptionValue(); pullInstance(subscriptionKey, value, false); this.instancesCache.put(subscriptionKey, value); } } } } public List getInstanceCache(SubscriptionKey key) { return this.instancesCache.get(key).instancesCache; } @Subscribe public void onPullInstanceEvent(PullInstanceEvent event) { // to avoid too many pulls queued. if (pullInstanceTaskOnceInProgress) { return; } pullInstanceTaskOnceInProgress = true; startTask(new PullInstanceOnceTask()); } private void pullInstance(SubscriptionKey k, SubscriptionValue v, boolean sendChangedEvent) { if (myselfServiceId == null) { // registration not ready return; } try { FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient .findMicroserviceInstance(myselfServiceId, k.appId, k.serviceName, ALL_VERSION, v.revision); if (instancesResponse.isModified()) { List instances = instancesResponse.getMicroserviceInstancesResponse().getInstances() == null ? Collections.emptyList() : instancesResponse.getMicroserviceInstancesResponse().getInstances(); setMicroserviceInfo(instances); LOGGER.info("Instance changed event. " + "Current: revision={}, instances={}. " + "Origin: revision={}, instances={}. " + "appId={}, serviceName={}.", instancesResponse.getRevision(), instanceToString(instances), v.revision, instanceToString(v.instancesCache), k.appId, k.serviceName ); v.instancesCache = instances; v.revision = instancesResponse.getRevision(); if (sendChangedEvent) { eventBus.post(new InstanceChangedEvent(k.appId, k.serviceName, v.instancesCache)); } } } catch (Exception e) { LOGGER.warn("find service {}#{} instance failed, remaining local instances cache [{}], cause message: {}", k.appId, k.serviceName, instanceToString(v.instancesCache), e.getMessage()); } } private void setMicroserviceInfo(List instances) { instances.forEach(instance -> { Microservice microservice = microserviceCache .computeIfAbsent(instance.getServiceId(), id -> { try { return serviceCenterClient.getMicroserviceByServiceId(id); } catch (Exception e) { LOGGER.error("Find microservice by id={} failed", id, e); throw e; } }); instance.setMicroservice(microservice); }); } class PullInstanceTask implements Task { @Override public void execute() { pullAllInstance(); startTask(new BackOffSleepTask(buildPollIntervalWithSalt(), new PullInstanceTask())); } } private long buildPollIntervalWithSalt() { int positive = random.nextInt(5); int sign = random.nextBoolean() ? 1 : -1; long currentPollInterval = pollInterval + sign * positive * 1000; return currentPollInterval > 0 ? currentPollInterval : pollInterval; } class PullInstanceOnceTask implements Task { @Override public void execute() { try { pullAllInstance(); } finally { pullInstanceTaskOnceInProgress = false; } } } private synchronized void pullAllInstance() { instancesCache.forEach((k, v) -> { pullInstance(k, v, true); }); } private static String instanceToString(List instances) { if (instances == null) { return ""; } StringBuilder sb = new StringBuilder(); for (MicroserviceInstance instance : instances) { for (String endpoint : instance.getEndpoints()) { sb.append(endpoint.length() > 64 ? endpoint.substring(0, 64) : endpoint); sb.append("|"); } sb.append(instance.getStatus()); sb.append("|"); } return sb.toString(); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.util.List; import java.util.Map; import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; import org.apache.servicecomb.service.center.client.model.MicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.MicroservicesResponse; import org.apache.servicecomb.service.center.client.model.ModifySchemasRequest; import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; import org.apache.servicecomb.service.center.client.model.SchemaInfo; public interface ServiceCenterOperation { /** * Query service center instances. * * In configuration file, only one address of service center instances can be configured, and * other instances can be discovered by calling this method. * * @return MicroserviceInstancesResponse * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ MicroserviceInstancesResponse getServiceCenterInstances(); /** * Register microservcie. * * @return RegisterMicroserviceResponse * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ RegisteredMicroserviceResponse registerMicroservice(Microservice microservice); /** * find all registered microservice of service-center. * * @return MicroserviceResponse * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ MicroservicesResponse getMicroserviceList(); /** * Get microservice information by service id. * * @return Microservice * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ Microservice getMicroserviceByServiceId(String serviceId); /** * query Microservice ID by appId, serviceName, version and environment. * * @return Microservice ID, null if microservice does not exists. * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ RegisteredMicroserviceResponse queryServiceId(Microservice microservice); /** * Register microservice instances to service-center * * Notice: microserviceInstance' service id must be set before calling this method. * * @return instanceId * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ RegisteredMicroserviceInstanceResponse registerMicroserviceInstance(MicroserviceInstance microserviceInstance); /** * Find microservice instances of service-center * * @return MicroserviceInstancesResponse * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ MicroserviceInstancesResponse getMicroserviceInstanceList(String serviceId); /** * Get microservice instance by id * * @return MicroserviceInstance * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ MicroserviceInstance getMicroserviceInstance(String serviceId, String instanceId); /** * Find MicroserviceInstance by properties. * * @return FindMicroserviceInstancesResponse * @throws OperationException If some problems happened to contact service center or non http 200 returned.n */ FindMicroserviceInstancesResponse findMicroserviceInstance(String consumerId, String appId, String serviceName, String versionRule, String revision); /** * Delete a microservice instance * * @throws OperationException */ void deleteMicroserviceInstance(String serviceId, String instanceId); /** * Update status of microservice Instance * * @return if update is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, MicroserviceInstanceStatus status); /** * register schema. * @return if register is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ boolean registerSchema(String serviceId, String schemaId, CreateSchemaRequest schema); /** * update schema context of service * * @return if update is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ boolean updateServiceSchemaContext(String serviceId, SchemaInfo schemaInfo); /** * batch update schema context of service * * @return if update is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRequest modifySchemasRequest); /** * Batch send heartbeats to service-center * * @param heartbeatsRequest * @throws OperationException */ void sendHeartBeats(HeartbeatsRequest heartbeatsRequest); /** * send heart beat of this instance. * @return if heartbeat is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ boolean sendHeartBeat(String serviceId, String instanceId); /** * Get schemas list of service * * @throws OperationException */ List getServiceSchemasList(String serviceId, boolean withContent); /** * query token using user confidential * * @return if heartbeat is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ RbacTokenResponse queryToken(RbacTokenRequest request, String host); /** * Update properties of microservice * * @return if update is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties, Framework framework); /** * Check serviceCenter isolation address available */ void checkIsolationAddressAvailable(); } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; public class ServiceCenterRawClient { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterRawClient.class); private static final String HEADER_TENANT_NAME = "x-domain-name"; private static final String ADDRESS_CHECK_PATH = "/v4/default/registry/health/readiness"; private final String tenantName; private final HttpTransport httpTransport; private final ServiceCenterAddressManager addressManager; private EventBus eventBus; private ServiceCenterRawClient(String tenantName, HttpTransport httpTransport, ServiceCenterAddressManager addressManager) { this.httpTransport = httpTransport; this.tenantName = tenantName; this.addressManager = addressManager; } public void setEventBus(EventBus eventBus) { this.eventBus = eventBus; } public HttpResponse getHttpRequest(String url, Map headers, String content) throws IOException { return doHttpRequest(url, false, headers, content, HttpRequest.GET, ""); } public HttpResponse postHttpRequestAbsoluteUrl(String url, Map headers, String content, String address) throws IOException { return doHttpRequest(url, true, headers, content, HttpRequest.POST, address); } public HttpResponse postHttpRequest(String url, Map headers, String content) throws IOException { return doHttpRequest(url, false, headers, content, HttpRequest.POST, ""); } public HttpResponse putHttpRequest(String url, Map headers, String content) throws IOException { return doHttpRequest(url, false, headers, content, HttpRequest.PUT, ""); } public HttpResponse deleteHttpRequest(String url, Map headers, String content) throws IOException { return doHttpRequest(url, false, headers, content, HttpRequest.DELETE, ""); } private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map headers, String content, String method, String queryAddress) throws IOException { String address = StringUtils.isEmpty(queryAddress) ? addressManager.address() : queryAddress; String formatUrl = addressManager.formatUrl(url, absoluteUrl, address); HttpRequest httpRequest = buildHttpRequest(formatUrl, headers, content, method); HttpResponse httpResponse; try { httpResponse = httpTransport.doRequest(httpRequest); recordAndSendUnAuthorizedEvent(httpResponse, address); return httpResponse; } catch (IOException e) { addressManager.recordFailState(address); String retryAddress = addressManager.address(); formatUrl = addressManager.formatUrl(url, absoluteUrl, retryAddress); LOGGER.warn("send request to {} failed and retry to {} once. ", address, retryAddress, e); httpRequest = new HttpRequest(formatUrl, headers, content, method); try { httpResponse = httpTransport.doRequest(httpRequest); recordAndSendUnAuthorizedEvent(httpResponse, retryAddress); return httpResponse; } catch (IOException ioException) { addressManager.recordFailState(retryAddress); LOGGER.warn("retry to {} failed again. ", retryAddress, e); throw ioException; } } } private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { LOGGER.warn("request unauthorized from server [{}], message [{}]", address, response.getMessage()); addressManager.recordFailState(address); this.eventBus.post(new UnAuthorizedOperationEvent(address)); } else { addressManager.recordSuccessState(address); } } public void checkAddressAvailable(String address) { ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); } private HttpRequest buildHttpRequest(String url, Map headers, String content, String method) { if (headers == null) { headers = new HashMap<>(); } headers.put(HEADER_TENANT_NAME, tenantName); return new HttpRequest(url, headers, content, method); } public static class Builder { private String tenantName; private HttpTransport httpTransport; private ServiceCenterAddressManager addressManager; public Builder() { } public Builder setTenantName(String tenantName) { this.tenantName = tenantName; return this; } public Builder setHttpTransport(HttpTransport httpTransport) { this.httpTransport = httpTransport; return this; } public Builder setAddressManager(ServiceCenterAddressManager addressManager) { this.addressManager = addressManager; return this; } public ServiceCenterRawClient build() { return new ServiceCenterRawClient(tenantName, httpTransport, addressManager); } } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; import org.apache.servicecomb.service.center.client.RegistrationEvents.HeartBeatEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceRegistrationEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.SchemaRegistrationEvent; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.apache.servicecomb.service.center.client.model.ServiceCenterConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; public class ServiceCenterRegistration extends AbstractTask { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterRegistration.class); public static final int MAX_INTERVAL = 600000; public static final int MIN_INTERVAL = 1000; private final ServiceCenterClient serviceCenterClient; private final EventBus eventBus; private Microservice microservice; private MicroserviceInstance microserviceInstance; private List schemaInfos = new ArrayList<>(); private final ServiceCenterConfiguration serviceCenterConfiguration; private long heartBeatInterval = 15000; private long heartBeatRequestTimeout = 5000; public ServiceCenterRegistration(ServiceCenterClient serviceCenterClient, ServiceCenterConfiguration serviceCenterConfiguration, EventBus eventBus) { super("service-center-registration-task"); this.serviceCenterClient = serviceCenterClient; this.serviceCenterConfiguration = serviceCenterConfiguration; this.eventBus = eventBus; } public ServiceCenterRegistration setMicroserviceInstance(MicroserviceInstance microserviceInstance) { this.microserviceInstance = microserviceInstance; return this; } public ServiceCenterRegistration setMicroservice(Microservice microservice) { this.microservice = microservice; return this; } public ServiceCenterRegistration setHeartBeatInterval(long interval) { if (interval > MAX_INTERVAL || interval < MIN_INTERVAL) { return this; } this.heartBeatInterval = interval; return this; } public ServiceCenterRegistration setHeartBeatRequestTimeout(long timeout) { if (timeout > MAX_INTERVAL || timeout < MIN_INTERVAL) { return this; } this.heartBeatRequestTimeout = timeout; return this; } public ServiceCenterRegistration setSchemaInfos(List schemaInfos) { this.schemaInfos = schemaInfos; return this; } public ServiceCenterRegistration addSchemaInfo(SchemaInfo schemaInfo) { this.schemaInfos.add(schemaInfo); return this; } public List getSchemaInfos() { return this.schemaInfos; } public void startRegistration() { startTask(new RegisterMicroserviceTask(0)); schedulerCheckAddressAvailable("sc-addr-check", new CheckAddressTask(), heartBeatInterval); } class RegisterMicroserviceTask implements Task { int failedCount; RegisterMicroserviceTask(int failedCount) { this.failedCount = failedCount; } @Override public void execute() { try { RegisteredMicroserviceResponse serviceResponse = serviceCenterClient.queryServiceId(microservice); if (serviceResponse == null) { RegisteredMicroserviceResponse response = serviceCenterClient.registerMicroservice(microservice); if (StringUtils.isEmpty(response.getServiceId())) { LOGGER.error("register microservice failed, and will try again."); eventBus.post(new MicroserviceRegistrationEvent(false, microservice)); startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceTask(failedCount + 1))); return; } microservice.setServiceId(response.getServiceId()); microserviceInstance.setServiceId(response.getServiceId()); microserviceInstance.setMicroservice(microservice); eventBus.post(new MicroserviceRegistrationEvent(true, microservice)); startTask(new RegisterSchemaTask(0)); } else { Microservice newMicroservice = serviceCenterClient.getMicroserviceByServiceId(serviceResponse.getServiceId()); Map propertiesTemp = microservice.getProperties(); microservice.setProperties(newMicroservice.getProperties()); microservice.getProperties().putAll(propertiesTemp); if (serviceCenterClient.updateMicroserviceProperties(serviceResponse.getServiceId(), microservice.getProperties(), microservice.getFramework())) { LOGGER.info( "microservice is already registered. Update microservice properties successfully. properties=[{}], " + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); } else { LOGGER.error("microservice is already registered. Update microservice properties failed. properties=[{}], " + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); } microservice.setServiceId(serviceResponse.getServiceId()); microserviceInstance.setServiceId(serviceResponse.getServiceId()); microserviceInstance.setMicroservice(microservice); if (isSwaggerDifferent(newMicroservice)) { if (serviceCenterConfiguration.isCanOverwriteSwagger()) { LOGGER.warn("Service has already registered, but schema ids not equal, try to register it again"); eventBus.post(new MicroserviceRegistrationEvent(true, microservice)); startTask(new RegisterSchemaTask(0)); return; } if (serviceCenterConfiguration.isIgnoreSwaggerDifferent()) { LOGGER.warn("Service has already registered, but schema ids not equal. Ignore and continue to register"); } else { throw new IllegalStateException( "Service has already registered, but schema ids not equal, stop register. " + "Change the microservice version or delete the old microservice info and try again."); } } eventBus.post(new MicroserviceRegistrationEvent(true, microservice)); startTask(new RegisterMicroserviceInstanceTask(0)); } } catch (IllegalStateException e) { throw e; } catch (Exception e) { LOGGER.error("register microservice failed, and will try again.", e); eventBus.post(new MicroserviceRegistrationEvent(false, microservice)); startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceTask(failedCount + 1))); } } } private boolean isSwaggerDifferent(Microservice newMicroservice) { return !isListEquals(newMicroservice.getSchemas(), microservice.getSchemas()); } private boolean isListEquals(List one, List two) { return one.size() == two.size() && one.containsAll(two) && two.containsAll(one); } class RegisterSchemaTask implements Task { int failedCount; RegisterSchemaTask(int failedCount) { this.failedCount = failedCount; } @Override public void execute() { try { if (schemaInfos == null || schemaInfos.isEmpty()) { LOGGER.warn("no schemas defined for this microservice."); eventBus.post(new SchemaRegistrationEvent(true, microservice)); startTask(new RegisterMicroserviceInstanceTask(0)); return; } for (SchemaInfo schemaInfo : schemaInfos) { CreateSchemaRequest request = new CreateSchemaRequest(); request.setSchema(schemaInfo.getSchema()); request.setSummary(schemaInfo.getSummary()); if (!serviceCenterClient.registerSchema(microservice.getServiceId(), schemaInfo.getSchemaId(), request)) { LOGGER.error("register schema content failed, and will try again."); eventBus.post(new SchemaRegistrationEvent(false, microservice)); // back off by multiply startTask(new BackOffSleepTask(failedCount + 1, new RegisterSchemaTask((failedCount + 1) * 2))); return; } } eventBus.post(new SchemaRegistrationEvent(true, microservice)); startTask(new RegisterMicroserviceInstanceTask(0)); } catch (Exception e) { LOGGER.error("register schema content failed, and will try again.", e); eventBus.post(new SchemaRegistrationEvent(false, microservice)); // back off by multiply startTask(new BackOffSleepTask(failedCount + 1, new RegisterSchemaTask((failedCount + 1) * 2))); } } } class RegisterMicroserviceInstanceTask implements Task { int failedCount; RegisterMicroserviceInstanceTask(int failedCount) { this.failedCount = failedCount; } @Override public void execute() { try { RegisteredMicroserviceInstanceResponse instance = serviceCenterClient .registerMicroserviceInstance(microserviceInstance); if (instance == null) { LOGGER.error("register microservice instance failed, and will try again."); eventBus.post(new MicroserviceInstanceRegistrationEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceInstanceTask(failedCount + 1))); } else { microserviceInstance.setInstanceId(instance.getInstanceId()); LOGGER.info("register microservice successfully, service id={}, instance id={}", microservice.getServiceId(), microserviceInstance.getInstanceId()); eventBus.post(new MicroserviceInstanceRegistrationEvent(true, microservice, microserviceInstance)); startTask(new SendHeartBeatTask(0)); } } catch (Exception e) { LOGGER.error("register microservice instance failed, and will try again.", e); eventBus.post(new MicroserviceInstanceRegistrationEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceInstanceTask(failedCount + 1))); } } } class SendHeartBeatTask implements Task { private static final int FAILED_RETRY = 3; int failedCount; SendHeartBeatTask(int failedCount) { this.failedCount = failedCount; } @Override public void execute() { try { if (failedCount >= FAILED_RETRY) { eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new RegisterMicroserviceTask(0)); return; } if (!serviceCenterClient.sendHeartBeat(microservice.getServiceId(), microserviceInstance.getInstanceId())) { LOGGER.warn("send heart failed, and will try again."); eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new SendHeartBeatTask(failedCount + 1))); } else { // wait 10 * 3000 ms and send heart beat again. eventBus.post(new HeartBeatEvent(true, microservice, microserviceInstance)); startTask( new BackOffSleepTask(Math.max(heartBeatInterval, heartBeatRequestTimeout), new SendHeartBeatTask(0))); } } catch (Exception e) { // If heartbeat failures three times, log error stack help troubleshooting. Others just log message as warn. if (failedCount == 2) { LOGGER.error("send heart failed, and will try again.", e); } else { LOGGER.warn("send heart failed, and will try again. message [{}]", e.getMessage()); } eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new SendHeartBeatTask(failedCount + 1))); } } } class CheckAddressTask implements Runnable { @Override public void run() { serviceCenterClient.checkIsolationAddressAvailable(); } } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.auth.SignRequest; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.http.client.common.WebSocketListener; import org.apache.servicecomb.http.client.common.WebSocketTransport; import org.apache.servicecomb.service.center.client.DiscoveryEvents.PullInstanceEvent; import org.java_websocket.handshake.ServerHandshake; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; public class ServiceCenterWatch implements WebSocketListener { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterWatch.class); private static final String HTTP = "http://"; private static final String HTTPS = "https://"; private static final String WS = "ws://"; private static final String WSS = "wss://"; private static final String WATCH = "/v4/%s/registry/microservices/%s/watcher"; private static final long SLEEP_BASE = 3000; private static final long SLEEP_MAX = 10 * 60 * 10000; private final ServiceCenterAddressManager addressManager; private final SSLProperties sslProperties; private final RequestAuthHeaderProvider requestAuthHeaderProvider; private final String tenantName; private final Map extraGlobalHeaders; private WebSocketTransport webSocketTransport; private String project; private String serviceId; private AtomicInteger continuousError = new AtomicInteger(0); private final AtomicBoolean reconnecting = new AtomicBoolean(false); private final EventBus eventBus; private String currentServerUri; private final ExecutorService connector = Executors.newFixedThreadPool(1, (r) -> new Thread(r, "web-socket-connector")); public ServiceCenterWatch(ServiceCenterAddressManager addressManager, SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider, String tenantName, Map extraGlobalHeaders, EventBus eventBus) { this.addressManager = addressManager; this.sslProperties = sslProperties; this.requestAuthHeaderProvider = requestAuthHeaderProvider; this.tenantName = tenantName; this.extraGlobalHeaders = extraGlobalHeaders; this.eventBus = eventBus; } public void startWatch(String project, String serviceId) { this.project = project; this.serviceId = serviceId; startWatch(); } private void startWatch() { connector.submit(() -> { backOff(); String address = addressManager.address(); try { Map headers = new HashMap<>(); headers.put("x-domain-name", this.tenantName); headers.putAll(this.extraGlobalHeaders); headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(createSignRequest(address))); currentServerUri = convertAddress(address); LOGGER.info("start watch to address {}", currentServerUri); webSocketTransport = new WebSocketTransport(currentServerUri, sslProperties, headers, this); webSocketTransport.connectBlocking(); addressManager.recordSuccessState(address); } catch (Exception e) { addressManager.recordFailState(address); LOGGER.error("start watch failed. ", e); } }); } private SignRequest createSignRequest(String url) { try { URI uri = URI.create(url); SignRequest signRequest = new SignRequest(); signRequest.setEndpoint(uri); return signRequest; } catch (Exception e) { return null; } } private String convertAddress(String address) { String url = String.format(WATCH, project, serviceId); if (address.startsWith(HTTP)) { return WS + address.substring(HTTP.length()) + url; } if (address.startsWith(HTTPS)) { return WSS + address.substring(HTTPS.length()) + url; } return address + url; } public void stop() { if (webSocketTransport != null) { webSocketTransport.close(); } } private void reconnect() { if (reconnecting.getAndSet(true)) { return; } continuousError.incrementAndGet(); if (webSocketTransport != null) { webSocketTransport.close(); } startWatch(); } private void backOff() { if (this.continuousError.get() <= 0) { return; } try { Thread.sleep(Math.min(SLEEP_MAX, this.continuousError.get() * this.continuousError.get() * SLEEP_BASE)); } catch (InterruptedException e) { // do not care } } @Override public void onMessage(String s) { LOGGER.info("web socket receive message [{}], start query instance", s); this.eventBus.post(new PullInstanceEvent()); } @Override public void onError(Exception e) { LOGGER.warn("web socket receive error [{}], will restart.", e.getMessage()); reconnect(); } @Override public void onClose(int code, String reason, boolean remote) { LOGGER.warn("web socket closed, code={}, reason={}.", code, reason); } @Override public void onOpen(ServerHandshake serverHandshake) { LOGGER.info("web socket connected to server {}, status={}, message={}", currentServerUri, serverHandshake.getHttpStatus(), serverHandshake.getHttpStatusMessage()); continuousError.set(0); reconnecting.set(false); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/exception/OperationException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.exception; public class OperationException extends RuntimeException { private static final long serialVersionUID = 1L; public OperationException() { } public OperationException(String message) { super(message); } public OperationException(String message, Throwable cause) { super(message, cause); } public OperationException(Throwable cause) { super(cause); } public OperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/BasePath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.Map; public class BasePath { private String path; private Map property; public Map getProperty() { return property; } public void setProperty(Map property) { this.property = property; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceInstanceRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class CreateMicroserviceInstanceRequest { private MicroserviceInstance instance; public MicroserviceInstance getInstance() { return instance; } public void setInstance(MicroserviceInstance instance) { this.instance = instance; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class CreateMicroserviceRequest { private Microservice service; public Microservice getService() { return service; } public void setService(Microservice service) { this.service = service; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateSchemaRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class CreateSchemaRequest { private String schema; private String summary; public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/DataCenterInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class DataCenterInfo { private String name; private String region; private String availableZone; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRegion() { return region; } public void setRegion(String region) { this.region = region; } public String getAvailableZone() { return availableZone; } public void setAvailableZone(String availableZone) { this.availableZone = availableZone; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ErrorMessage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class ErrorMessage { private String errorCode; private String errorMessage; private String detail; public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/FindMicroserviceInstancesResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class FindMicroserviceInstancesResponse { private boolean modified; private String revision; private MicroserviceInstancesResponse microserviceInstancesResponse; public boolean isModified() { return modified; } public void setModified(boolean modified) { this.modified = modified; } public String getRevision() { return revision; } public void setRevision(String revision) { this.revision = revision; } public MicroserviceInstancesResponse getMicroserviceInstancesResponse() { return microserviceInstancesResponse; } public void setMicroserviceInstancesResponse( MicroserviceInstancesResponse microserviceInstancesResponse) { this.microserviceInstancesResponse = microserviceInstancesResponse; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Framework.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class Framework { private String name; private String version; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaListResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.List; public class GetSchemaListResponse { private List schemas; public List getSchemas() { return schemas; } public void setSchemas(List schemas) { this.schemas = schemas; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class GetSchemaResponse { private String schema; public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheck.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class HealthCheck { private HealthCheckMode mode; private int port; private int interval; private int times; public HealthCheckMode getMode() { return mode; } public void setMode(HealthCheckMode mode) { this.mode = mode; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getInterval() { return interval; } public void setInterval(int interval) { this.interval = interval; } public int getTimes() { return times; } public void setTimes(int times) { this.times = times; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheckMode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public enum HealthCheckMode { unknown, push, pull } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HeartbeatsRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.ArrayList; import java.util.List; public class HeartbeatsRequest { private List Instances; public HeartbeatsRequest(String serviceId, String instanceId) { List instances = new ArrayList<>(); instances.add(new InstancesRequest(serviceId, instanceId)); this.Instances = instances; } public List getInstances() { return Instances; } public void setInstances(List instances) { this.Instances = instances; } public void addInstances(InstancesRequest instancesRequest) { if (this.Instances != null) { this.Instances.add(instancesRequest); } else { this.Instances = new ArrayList<>(); this.Instances.add(instancesRequest); } } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/InstancesRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class InstancesRequest { private String serviceId; private String instanceId; public InstancesRequest(String serviceId, String instanceId) { this.serviceId = serviceId; this.instanceId = instanceId; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Microservice.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonRootName; @JsonRootName("service") public class Microservice { private String serviceId; private Framework framework; private String registerBy; private String environment; private String appId; private String serviceName; /** * for governance * when invoke cross app, if not use alias name, then {microservice}:{schema}:{operation} will conflict */ private String alias; private String version; private String description; private String level; private List schemas = new ArrayList<>(); private List paths = new ArrayList<>(); private MicroserviceStatus status = MicroserviceStatus.UP; private Map properties = new HashMap<>(); private List instances; private String timestamp; private String modTimestamp; public Microservice() { } public Microservice(String serviceName) { this.serviceName = serviceName; } public List getInstances() { return instances; } public void setInstances(List instances) { this.instances = instances; } public Framework getFramework() { return framework; } public void setFramework(Framework framework) { this.framework = framework; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public String getRegisterBy() { return registerBy; } public void setRegisterBy(String registerBy) { this.registerBy = registerBy; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLevel() { return level; } public void setLevel(String level) { this.level = level; } public List getSchemas() { return schemas; } public void setSchemas(List schemas) { this.schemas = schemas; } public MicroserviceStatus getStatus() { return status; } public void setStatus(MicroserviceStatus status) { this.status = status; } public Map getProperties() { return properties; } public void setProperties(Map properties) { this.properties = properties; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getModTimestamp() { return modTimestamp; } public void setModTimestamp(String modTimestamp) { this.modTimestamp = modTimestamp; } public List getPaths() { return paths; } public void setPaths(List paths) { this.paths = paths; } public void addSchema(String schema) { this.schemas.add(schema); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonRootName; @JsonRootName("instance") public class MicroserviceInstance { // even disconnected from service center // instanceId will not be changed // when register to service center again, use the old instanceId. private String instanceId; private String serviceId; private String version; private List endpoints = new ArrayList<>(); private String hostName; private MicroserviceInstanceStatus status; private Map properties = new HashMap<>(); // reserved key list: region|az|stage|group private HealthCheck healthCheck; private DataCenterInfo dataCenterInfo; private String timestamp; private String modTimestamp; @JsonIgnore private Microservice microservice; public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public List getEndpoints() { return endpoints; } public void setEndpoints(List endpoints) { this.endpoints = endpoints; } public String getHostName() { return hostName; } public void setHostName(String hostName) { this.hostName = hostName; } public MicroserviceInstanceStatus getStatus() { return status; } public void setStatus(MicroserviceInstanceStatus status) { this.status = status; } public HealthCheck getHealthCheck() { return healthCheck; } public void setHealthCheck(HealthCheck healthCheck) { this.healthCheck = healthCheck; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getModTimestamp() { return modTimestamp; } public void setModTimestamp(String modTimestamp) { this.modTimestamp = modTimestamp; } public Map getProperties() { return properties; } public void setProperties(Map properties) { this.properties = properties; } public DataCenterInfo getDataCenterInfo() { return dataCenterInfo; } public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { this.dataCenterInfo = dataCenterInfo; } public void setMicroservice(Microservice microservice) { this.microservice = microservice; } @JsonIgnore public Microservice getMicroservice() { return this.microservice; } @JsonIgnore public String getServiceName() { return this.microservice.getServiceName(); } @JsonIgnore public String getApplicationName() { return this.microservice.getAppId(); } public void addEndpoint(String endpoint) { this.endpoints.add(endpoint); } public void addProperty(String key, String value) { this.properties.put(key, value); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class MicroserviceInstanceResponse { private MicroserviceInstance instance; public MicroserviceInstance getInstance() { return instance; } public void setInstance(MicroserviceInstance instance) { this.instance = instance; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public enum MicroserviceInstanceStatus { STARTING, TESTING, UP, OUTOFSERVICE, DOWN, UNKNOWN } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstancesResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.List; public class MicroserviceInstancesResponse { private List instances; public List getInstances() { return instances; } public void setInstances(List instances) { this.instances = instances; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class MicroserviceResponse { private Microservice service; public Microservice getService() { return service; } public void setService(Microservice service) { this.service = service; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public enum MicroserviceStatus { UNKNOWN, UP, DOWN } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroservicesResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.List; public class MicroservicesResponse { private List services; public List getServices() { return services; } public void setServices(List services) { this.services = services; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ModifySchemasRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.List; public class ModifySchemasRequest { private List schemas; public List getSchemas() { return schemas; } public void setSchemas(List schemas) { this.schemas = schemas; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class RbacTokenRequest { private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { final StringBuilder sb = new StringBuilder("RbacTokenRequest{"); sb.append("name='").append(name).append('\''); sb.append(", password='").append(password).append('\''); sb.append('}'); return sb.toString(); } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class RbacTokenResponse { private int statusCode; private String token; private String errorCode; public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceInstanceResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class RegisteredMicroserviceInstanceResponse { private String instanceId; public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class RegisteredMicroserviceResponse { private String serviceId; public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/SchemaInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class SchemaInfo { private String schema; private String schemaId; private String summary; public SchemaInfo() { } public SchemaInfo(String schemaId, String schema, String summary) { this.schemaId = schemaId; this.schema = schema; this.summary = summary; } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public String getSchemaId() { return schemaId; } public void setSchemaId(String schemaId) { this.schemaId = schemaId; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ServiceCenterConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; public class ServiceCenterConfiguration { /** * for registration service * when swagger is different between local with remote serviceCenter. if ignoreSwaggerDifferent is true. * it will ignore the different and continue the program. otherwise, the program will be stop. */ private boolean ignoreSwaggerDifferent; private boolean canOverwriteSwagger = true; public boolean isIgnoreSwaggerDifferent() { return ignoreSwaggerDifferent; } public boolean isCanOverwriteSwagger(){ return canOverwriteSwagger; } public ServiceCenterConfiguration setIgnoreSwaggerDifferent(boolean ignoreSwaggerDifferent) { this.ignoreSwaggerDifferent = ignoreSwaggerDifferent; return this; } public ServiceCenterConfiguration setCanOverwriteSwagger(boolean canOverwriteSwagger) { this.canOverwriteSwagger = canOverwriteSwagger; return this; } } ================================================ FILE: clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client.model; import java.util.Map; public class UpdatePropertiesRequest { private Map properties; private Framework framework; public Map getProperties() { return properties; } public void setProperties(Map properties) { this.properties = properties; } public Framework getFramework() { return framework; } public void setFramework(Framework framework) { this.framework = framework; } } ================================================ FILE: clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import com.google.common.eventbus.EventBus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ServiceCenterAddressManagerTest { private static final List addresses = new ArrayList<>(); private static ServiceCenterAddressManager addressManager1; private static ServiceCenterAddressManager addressManager2; @Test public void getUrlPrefix() { addresses.add("http://127.0.0.1:30103"); addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); Assertions.assertNotNull(addressManager1); List addresses = addressManager1.getAddresses(); Assertions.assertEquals(1, addresses.size()); Assertions.assertEquals("http://127.0.0.1:30103", addresses.get(0)); Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103/v4/", addressManager1.getUrlPrefix("http://127.0.0.1:30103")); } @Test public void formatUrlTest() { addresses.add("http://127.0.0.1:30103"); addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); Assertions.assertNotNull(addressManager1); String address = addressManager1.address(); Assertions.assertEquals("http://127.0.0.1:30103", address); String url = addressManager1.formatUrl("/test/", false, address); Assertions.assertEquals("http://127.0.0.1:30103/v4/project/test/", url); url = addressManager1.formatUrl("/test/", true, address); Assertions.assertEquals("http://127.0.0.1:30103/test/", url); } @Test public void onRefreshEndpointEvent() { List addressAZ = new ArrayList<>(); addressAZ.add("http://127.0.0.3:30100"); List addressRG = new ArrayList<>(); addressRG.add("http://127.0.0.4:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "SERVICECENTER"); addressManager1.refreshEndpoint(event, "SERVICECENTER"); List availableZone = addressManager1.getAvailableZone(); Assertions.assertEquals("http://127.0.0.3:30100", availableZone.get(0)); List availableRegion = addressManager1.getAvailableRegion(); Assertions.assertEquals("http://127.0.0.4:30100", availableRegion.get(0)); } } ================================================ FILE: clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.InstancesRequest; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; import org.apache.servicecomb.service.center.client.model.MicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.MicroservicesResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.ArgumentMatchers; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.google.common.eventbus.EventBus; /** * Created by on 2019/10/17. */ public class ServiceCenterClientTest { private final ServiceCenterAddressManager addressManager; public ServiceCenterClientTest() { this.addressManager = new ServiceCenterAddressManager("default", Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); } @Test public void TestGetServiceCenterInstances() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); String responseString = "{\n" + " \"instances\": [\n" + " {\n" + " \"instanceId\": \"111111\",\n" + " \"serviceId\": \"222222\",\n" + " \"version\": \"1.0\",\n" + " \"hostName\": \"Test\",\n" + " \"endpoints\": [\n" + " \"string\"\n" + " ],\n" + " \"status\": \"UP\",\n" + " \"properties\": {\n" + " \"additionalProp1\": \"string\",\n" + " \"additionalProp2\": \"string\",\n" + " \"additionalProp3\": \"string\"\n" + " },\n" + " \"healthCheck\": {\n" + " \"mode\": \"push\",\n" + " \"port\": \"0\",\n" + " \"interval\": \"0\",\n" + " \"times\": \"0\"\n" + " },\n" + " \"dataCenterInfo\": {\n" + " \"name\": \"string\",\n" + " \"region\": \"string\",\n" + " \"availableZone\": \"string\"\n" + " },\n" + " \"timestamp\": \"333333\",\n" + " \"modTimestamp\": \"4444444\"\n" + " }\n" + " ]\n" + "}"; httpResponse.setContent(responseString); Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/health", null, null)).thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient.getServiceCenterInstances(); Assertions.assertNotNull(serviceCenterInstances); Assertions.assertEquals(1, serviceCenterInstances.getInstances().size()); Assertions.assertEquals("111111", serviceCenterInstances.getInstances().get(0).getInstanceId()); Assertions.assertEquals("222222", serviceCenterInstances.getInstances().get(0).getServiceId()); } @Test public void TestRegistryService() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); httpResponse.setContent("{\"serviceId\": \"111111\"}"); Microservice microservice = new Microservice(); microservice.setServiceName("Test"); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true); Mockito.when(serviceCenterRawClient .postHttpRequest(ArgumentMatchers.eq("/registry/microservices"), ArgumentMatchers.eq(null), Mockito.anyString())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); RegisteredMicroserviceResponse actualResponse = serviceCenterClient.registerMicroservice(microservice); Assertions.assertNotNull(actualResponse); Assertions.assertEquals("111111", actualResponse.getServiceId()); } @Test public void TestGetServiceMessage() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); String responseString = "{\n" + " \"service\": {\n" + " \"serviceId\": \"111111\",\n" + " \"environment\": \"string\",\n" + " \"appId\": \"string\",\n" + " \"serviceName\": \"string\",\n" + " \"version\": \"string\",\n" + " \"description\": \"string\",\n" + " \"level\": \"string\",\n" + " \"registerBy\": \"string\",\n" + " \"schemas\": [\n" + " \"string\"\n" + " ],\n" + " \"status\": \"UP\",\n" + " \"timestamp\": \"string\",\n" + " \"modTimestamp\": \"string\",\n" + " \"framework\": {\n" + " \"name\": \"string\",\n" + " \"version\": \"string\"\n" + " },\n" + " \"paths\": [\n" + " {\n" + " \"Path\": \"string\",\n" + " \"Property\": {\n" + " \"additionalProp1\": \"string\",\n" + " \"additionalProp2\": \"string\",\n" + " \"additionalProp3\": \"string\"\n" + " }\n" + " }\n" + " ],\n" + " \"properties\": {\n" + " \"additionalProp1\": \"string\",\n" + " \"additionalProp2\": \"string\",\n" + " \"additionalProp3\": \"string\"\n" + " }\n" + " }\n" + "}"; httpResponse.setContent(responseString); Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/111111", null, null)) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Microservice microservices = serviceCenterClient.getMicroserviceByServiceId("111111"); Assertions.assertNotNull(microservices); Assertions.assertEquals("111111", microservices.getServiceId()); } @Test public void TestGetServiceList() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); MicroservicesResponse microservicesResponse = new MicroservicesResponse(); List microserviceList = new ArrayList<>(); microserviceList.add(new Microservice("Test1")); microserviceList.add(new Microservice("Test2")); microserviceList.add(new Microservice("Test3")); microservicesResponse.setServices(microserviceList); ObjectMapper mapper = new ObjectMapper(); httpResponse.setContent(mapper.writeValueAsString(microservicesResponse)); Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroservicesResponse actualMicroservicesResponse = serviceCenterClient.getMicroserviceList(); Assertions.assertNotNull(actualMicroservicesResponse); Assertions.assertEquals(3, actualMicroservicesResponse.getServices().size()); Assertions.assertEquals("Test1", actualMicroservicesResponse.getServices().get(0).getServiceName()); } @Test public void TestQueryServiceId() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); httpResponse.setContent("{\"serviceId\": \"111111\"}"); Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Microservice microservice = new Microservice("Test111"); RegisteredMicroserviceResponse actualServiceId = serviceCenterClient.queryServiceId(microservice); Assertions.assertNotNull(actualServiceId); Assertions.assertEquals("111111", actualServiceId.getServiceId()); } @Test public void TestRegisterServiceInstance() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); httpResponse.setContent("{\"instanceId\": \"111111\"}"); MicroserviceInstance instance = new MicroserviceInstance(); instance.setInstanceId("111111"); instance.setServiceId("222222"); ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true); Mockito.when(serviceCenterRawClient.postHttpRequest(ArgumentMatchers.eq("/registry/microservices/222222/instances"), ArgumentMatchers.eq(null), Mockito.anyString())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); RegisteredMicroserviceInstanceResponse actualResponse = serviceCenterClient.registerMicroserviceInstance(instance); Assertions.assertNotNull(actualResponse); Assertions.assertEquals("111111", actualResponse.getInstanceId()); } @Test public void TestDeleteServiceInstance() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); Mockito.when(serviceCenterRawClient.deleteHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); serviceCenterClient.deleteMicroserviceInstance("111", "222"); } @Test public void TestGetServiceInstanceList() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); String responseString = "{\n" + " \"instances\": [\n" + " {\n" + " \"instanceId\": \"111111\",\n" + " \"serviceId\": \"222222\",\n" + " \"version\": \"1.0\",\n" + " \"hostName\": \"Test\",\n" + " \"endpoints\": [\n" + " \"string\"\n" + " ],\n" + " \"status\": \"UP\",\n" + " \"timestamp\": \"333333\",\n" + " \"modTimestamp\": \"4444444\"\n" + " }\n" + " ]\n" + "}"; httpResponse.setContent(responseString); Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/222222/instances", null, null)) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient .getMicroserviceInstanceList("222222"); Assertions.assertNotNull(serviceCenterInstances); Assertions.assertEquals(1, serviceCenterInstances.getInstances().size()); Assertions.assertEquals("111111", serviceCenterInstances.getInstances().get(0).getInstanceId()); Assertions.assertEquals("222222", serviceCenterInstances.getInstances().get(0).getServiceId()); } @Test public void TestGetServiceInstanceMessage() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); String responseString = "{\n" + " \"instance\": {\n" + " \"instanceId\": \"111\",\n" + " \"serviceId\": \"222\",\n" + " \"version\": \"1.0\",\n" + " \"hostName\": \"Test\",\n" + " \"endpoints\": [\n" + " \"string\"\n" + " ],\n" + " \"status\": \"UP\",\n" + " \"properties\": {\n" + " \"additionalProp1\": \"string\",\n" + " \"additionalProp2\": \"string\",\n" + " \"additionalProp3\": \"string\"\n" + " },\n" + " \"healthCheck\": {\n" + " \"mode\": \"push\",\n" + " \"port\": \"0\",\n" + " \"interval\": \"0\",\n" + " \"times\": \"0\"\n" + " },\n" + " \"dataCenterInfo\": {\n" + " \"name\": \"string\",\n" + " \"region\": \"string\",\n" + " \"availableZone\": \"string\"\n" + " },\n" + " \"timestamp\": \"333333\",\n" + " \"modTimestamp\": \"4444444\"\n" + " }\n" + "}"; httpResponse.setContent(responseString); Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstance responseInstance = serviceCenterClient .getMicroserviceInstance("111", "222"); Assertions.assertNotNull(responseInstance); Assertions.assertEquals("111", responseInstance.getInstanceId()); Assertions.assertEquals("Test", responseInstance.getHostName()); } @Test public void TestSendHeartBeats() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); HeartbeatsRequest heartbeatsRequest = new HeartbeatsRequest("001", "1001"); heartbeatsRequest.addInstances(new InstancesRequest("002", "1002")); ObjectMapper mapper = new ObjectMapper(); Mockito .when(serviceCenterRawClient.putHttpRequest("/registry/microservices/111/instances/222/heartbeat", null, null)) .thenReturn(httpResponse); Mockito.when(serviceCenterRawClient .putHttpRequest(ArgumentMatchers.eq("/registry/heartbeats"), ArgumentMatchers.eq(null), Mockito.anyString())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); serviceCenterClient.sendHeartBeats(heartbeatsRequest); } @Test public void TestUpdateServicesInstanceStatus() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Boolean result = serviceCenterClient .updateMicroserviceInstanceStatus("111", "222", MicroserviceInstanceStatus.UP); Assertions.assertNotNull(result); Assertions.assertEquals(true, result); } @Test public void TestGetServiceSchemas() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); String responseString = "{\n" + " \"schemas\": [\n" + " {\n" + " \"schemaId\": \"111111\",\n" + " \"schema\": \"test\",\n" + " \"summary\": \"test\"\n" + " }\n" + " ]\n" + "}"; httpResponse.setContent(responseString); Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); List schemaResponse = serviceCenterClient .getServiceSchemasList("111", false); ObjectMapper mapper = new ObjectMapper(); JsonNode jsonNode = mapper.readTree(mapper.writeValueAsString(schemaResponse)); Assertions.assertNotNull(jsonNode); Assertions.assertEquals("111111", jsonNode.get(0).get("schemaId").textValue()); Assertions.assertEquals("test", jsonNode.get(0).get("schema").textValue()); } @Test public void TestGetServiceSchemasContext() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); String responseString = "{\n" + " \"schema\": \"test context\"\n" + "}"; httpResponse.setContent(responseString); Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); String schemaContext = serviceCenterClient .getServiceSchemaContext("111", "222"); Assertions.assertNotNull(schemaContext); Assertions.assertEquals("test context", schemaContext); } @Test public void TestUpdateServiceSchema() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); boolean result = serviceCenterClient .updateServiceSchemaContext("111", new SchemaInfo()); Assertions.assertTrue(result); } @Test public void testUpdateMicroserviceProperties() throws IOException { ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setMessage("ok"); Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); boolean result = serviceCenterClient .updateMicroserviceProperties("111", new HashMap(), new Framework()); Assertions.assertTrue(result); } } ================================================ FILE: clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.service.center.client; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import com.google.common.eventbus.EventBus; /** * Created by on 2019/10/16. */ public class ServiceCenterRawClientTest { private static final String PROJECT_NAME = "default"; private static final String TENANT_NAME = "default"; @Test public void TestDefaultParameter() throws IOException { HttpTransport httpTransport = Mockito.mock(HttpTransport.class); ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(PROJECT_NAME, Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); ServiceCenterRawClient client = new ServiceCenterRawClient.Builder() .setHttpTransport(httpTransport) .setAddressManager(addressManager) .setTenantName(TENANT_NAME) .build(); HttpResponse httpResponse = new HttpResponse(); httpResponse.setStatusCode(200); httpResponse.setContent("ok"); Mockito.when(httpTransport.doRequest(Mockito.any())).thenReturn(httpResponse); HttpResponse actualGetResponse = client.getHttpRequest(null, null, null); HttpResponse actualPostResponse = client.postHttpRequest(null, null, null); HttpResponse actualPutResponse = client.putHttpRequest(null, null, null); HttpResponse actualDeleteResponse = client.putHttpRequest(null, null, null); Assertions.assertNotNull(actualGetResponse); Assertions.assertEquals("ok", actualGetResponse.getContent()); Assertions.assertNotNull(actualPostResponse); Assertions.assertEquals("ok", actualPostResponse.getContent()); Assertions.assertNotNull(actualPutResponse); Assertions.assertEquals("ok", actualPutResponse.getContent()); Assertions.assertNotNull(actualDeleteResponse); Assertions.assertEquals("ok", actualDeleteResponse.getContent()); } } ================================================ FILE: common/common-access-log/pom.xml ================================================ common org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 common-access-log Java Chassis::Common::CommonAccessLog org.apache.servicecomb java-chassis-core org.apache.servicecomb foundation-common org.apache.servicecomb common-rest org.apache.servicecomb transport-rest-client io.vertx vertx-codegen test ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.foundation.common.event.EventManager; public class AccessLogBootListener implements BootListener { private final AccessLogBootstrap accessLogBootstrap = new AccessLogBootstrap(); @Override public void onAfterRegistry(BootEvent event) { accessLogBootstrap.start(EventManager.getEventBus()); } @Override public void onBeforeClose(BootEvent event) { accessLogBootstrap.shutdown(); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import com.google.common.eventbus.EventBus; public class AccessLogBootstrap { private static final AccessLogConfig config = AccessLogConfig.INSTANCE; public void start(EventBus eventBus) { SPIServiceUtils.getSortedService(AccessLogInitializer.class) .forEach(initializer -> initializer.init(eventBus, config)); } public void shutdown() { SPIServiceUtils.getSortedService(AccessLogInitializer.class) .forEach(AccessLogInitializer::destroy); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public class AccessLogConfig { public static final String SERVER_BASE = "servicecomb.accesslog."; public static final String CLIENT_BASE = "servicecomb.accesslog.request."; public static final String SERVER_LOG_ENABLED = SERVER_BASE + "enabled"; public static final String SERVER_LOG_PATTERN = SERVER_BASE + "pattern"; public static final String CLIENT_LOG_ENABLED = CLIENT_BASE + "enabled"; public static final String CLIENT_LOG_PATTERN = CLIENT_BASE + "pattern"; public static final String DEFAULT_SERVER_PATTERN = "%h - - %t %r %s %B %D"; public static final String DEFAULT_CLIENT_PATTERN = "%h %SCB-transport - - %t %r %s %D"; public static final AccessLogConfig INSTANCE = new AccessLogConfig(); private boolean serverLogEnabled; private boolean clientLogEnabled; private String serverLogPattern; private String clientLogPattern; private AccessLogConfig() { init(); } private void init() { clientLogEnabled = LegacyPropertyFactory.getBooleanProperty(CLIENT_LOG_ENABLED, false); serverLogEnabled = LegacyPropertyFactory.getBooleanProperty(SERVER_LOG_ENABLED, false); clientLogPattern = LegacyPropertyFactory.getStringProperty(CLIENT_LOG_PATTERN, DEFAULT_CLIENT_PATTERN); serverLogPattern = LegacyPropertyFactory.getStringProperty(SERVER_LOG_PATTERN, DEFAULT_SERVER_PATTERN); } public boolean isServerLogEnabled() { return serverLogEnabled; } public boolean isClientLogEnabled() { return clientLogEnabled; } public String getServerLogPattern() { return serverLogPattern; } public String getClientLogPattern() { return clientLogPattern; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AccessLogConfiguration { @Bean public AccessLogBootListener scbAccessLogBootListener() { return new AccessLogBootListener(); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog; import com.google.common.eventbus.EventBus; public interface AccessLogInitializer { default int getOrder() { return 0; } void init(EventBus eventBus, AccessLogConfig accessLogConfig); default void destroy() {} } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.client; import org.apache.servicecomb.common.accessLog.AccessLogConfig; import org.apache.servicecomb.common.accessLog.AccessLogInitializer; import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ClientDefaultInitializer implements AccessLogInitializer { private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); private AccessLogGenerator accessLogGenerator; @Override public void init(EventBus eventBus, AccessLogConfig accessLogConfig) { if (!accessLogConfig.isClientLogEnabled()) { return; } accessLogGenerator = new AccessLogGenerator(accessLogConfig.getClientLogPattern()); eventBus.register(this); } @Subscribe @AllowConcurrentEvents public void onRequestOut(InvocationFinishEvent finishEvent) { if (InvocationType.CONSUMER.equals(finishEvent.getInvocation().getInvocationType())) { LOGGER.info(accessLogGenerator.generateClientLog(finishEvent)); } } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/AccessLogGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core; import java.util.List; import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.parser.AccessLogPatternParser; import org.apache.servicecomb.common.accessLog.core.parser.impl.VertxRestAccessLogPatternParser; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import com.google.common.collect.Iterables; import io.vertx.ext.web.RoutingContext; /* * Accept {@link AccessLogParam} and generate access log. *
* Each AccessLogParam for a line of access log. */ public class AccessLogGenerator { /* * traversal this array to generate access log segment. */ private final AccessLogItem[] accessLogItems; private final AccessLogPatternParser logPatternParser = new VertxRestAccessLogPatternParser(); @SuppressWarnings("unchecked") public AccessLogGenerator(String rawPattern) { List> accessLogItemList = logPatternParser.parsePattern(rawPattern); accessLogItems = Iterables.toArray(accessLogItemList, AccessLogItem.class); } public String generateServerLog(ServerAccessLogEvent accessLogEvent) { StringBuilder log = new StringBuilder(128); for (AccessLogItem accessLogItem : getAccessLogItems()) { accessLogItem.appendServerFormattedItem(accessLogEvent, log); } return log.toString(); } public String generateClientLog(InvocationFinishEvent finishEvent) { StringBuilder log = new StringBuilder(128); for (AccessLogItem accessLogItem : getAccessLogItems()) { accessLogItem.appendClientFormattedItem(finishEvent, log); } return log.toString(); } @VisibleForTesting AccessLogItem[] getAccessLogItems() { return accessLogItems; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/AccessLogItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; /* * Access log item represents the items supported in access log and request log printing. * It generate the segment of log according to {@link AccessLogParam} */ public interface AccessLogItem { default void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { } default void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; /** * Configurable dateTime element. */ public class ConfigurableDatetimeAccessItem implements AccessLogItem { public static final String DEFAULT_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz"; public static final Locale DEFAULT_LOCALE = Locale.US; private final ThreadLocal datetimeFormatHolder = new ThreadLocal<>(); private String pattern; private TimeZone timezone; private Locale locale; /** * all configuration is set to default value. */ public ConfigurableDatetimeAccessItem() { this(DEFAULT_DATETIME_PATTERN); } /** * the configurations not specified will get a default value. * @param config the format of configuration is "PATTERN|TIMEZONE|LOCALE" or "PATTERN". It depends on whether the config contains the separator "|" */ public ConfigurableDatetimeAccessItem(String config) { String[] configArr; if (config.contains("|")) { configArr = splitConfig(config); } else { // if there is no separator "|", regard configuration as pattern. configArr = new String[3]; configArr[0] = config; } if (3 != configArr.length) { throw new IllegalArgumentException( "wrong format of configuration, \"PATTERN|TIMEZONE|LOCALE\" is expected, but actually is \"" + config + "\""); } setConfigurations(configArr); } private String[] splitConfig(String config) { return config.split("\\|{1}?", -1); } private void setConfigurations(String[] configArr) { this.pattern = StringUtils.isEmpty(configArr[0]) ? DEFAULT_DATETIME_PATTERN : configArr[0]; this.timezone = StringUtils.isEmpty(configArr[1]) ? TimeZone.getDefault() : TimeZone.getTimeZone(configArr[1]); this.locale = StringUtils.isEmpty(configArr[2]) ? DEFAULT_LOCALE : Locale.forLanguageTag(configArr[2]); } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { doAppendFormattedItem(accessLogEvent.getMilliStartTime(), builder); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { long milliDuration = finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000; doAppendFormattedItem( finishEvent.getInvocation().getInvocationStageTrace().getStartInMillis() + milliDuration, builder); } private void doAppendFormattedItem(long milliStartTime, StringBuilder builder) { SimpleDateFormat dateFormat = getDatetimeFormat(); builder.append(dateFormat.format(new Date(milliStartTime))); } private SimpleDateFormat getDatetimeFormat() { SimpleDateFormat dateFormat = datetimeFormatHolder.get(); if (null == dateFormat) { dateFormat = new SimpleDateFormat(pattern, locale); dateFormat.setTimeZone(timezone); datetimeFormatHolder.set(dateFormat); } return dateFormat; } public String getPattern() { return pattern; } public TimeZone getTimezone() { return timezone; } public Locale getLocale() { return locale; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import java.util.Map.Entry; import java.util.Set; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.Cookie; import io.vertx.ext.web.RoutingContext; public class CookieAccessItem implements AccessLogItem { public static final String RESULT_NOT_FOUND = "-"; private final String varName; public CookieAccessItem(String varName) { this.varName = varName; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { Set cookies = accessLogEvent.getRoutingContext().request().cookies(); if (null == cookies) { builder.append(RESULT_NOT_FOUND); return; } for (Cookie cookie : cookies) { if (varName.equals(cookie.getName())) { builder.append(cookie.getValue()); return; } } builder.append(RESULT_NOT_FOUND); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getCookieMap()) { builder.append(RESULT_NOT_FOUND); return; } for (Entry entry : restRequestImpl.getCookieMap().entrySet()) { if (entry.getKey().equals(varName)) { builder.append(entry.getValue()); return; } } builder.append(RESULT_NOT_FOUND); } public String getVarName() { return varName; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; public class DurationMillisecondAccessItem implements AccessLogItem { @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append(accessLogEvent.getMilliEndTime() - accessLogEvent.getMilliStartTime()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { builder.append(finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; public class DurationSecondAccessItem implements AccessLogItem { @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append((accessLogEvent.getMilliEndTime() - accessLogEvent.getMilliStartTime()) / 1000); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { builder.append(finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000_000); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; public class FirstLineOfRequestAccessItem implements AccessLogItem { private static final HttpMethodAccessItem METHOD_ELEMENT = new HttpMethodAccessItem(); private static final UrlPathAccessItem URI_PATH_ONLY_ELEMENT = new UrlPathAccessItem(); private static final RequestProtocolAccessItem VERSION_OR_PROTOCOL_ELEMENT = new RequestProtocolAccessItem(); @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append("\""); METHOD_ELEMENT.appendServerFormattedItem(accessLogEvent, builder); builder.append(" "); URI_PATH_ONLY_ELEMENT.appendServerFormattedItem(accessLogEvent, builder); builder.append(" "); VERSION_OR_PROTOCOL_ELEMENT.appendServerFormattedItem(accessLogEvent, builder); builder.append("\""); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { builder.append("\""); METHOD_ELEMENT.appendClientFormattedItem(finishEvent, builder); builder.append(" "); URI_PATH_ONLY_ELEMENT.appendClientFormattedItem(finishEvent, builder); builder.append(" "); VERSION_OR_PROTOCOL_ELEMENT.appendClientFormattedItem(finishEvent, builder); builder.append("\""); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; /** * HTTP method */ public class HttpMethodAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || null == request.method()) { builder.append(EMPTY_RESULT); return; } builder.append(request.method().toString()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta(); if (operationMeta != null && !StringUtils.isEmpty(operationMeta.getHttpMethod())) { builder.append(operationMeta.getHttpMethod()); return; } RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() || null == restRequestImpl.getHttpClientRequest().getMethod()) { builder.append(EMPTY_RESULT); return; } builder.append(restRequestImpl.getHttpClientRequest().getMethod().toString()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.swagger.invocation.Response; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class HttpStatusAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerResponse response = accessLogEvent.getRoutingContext().response(); if (null == response) { builder.append(EMPTY_RESULT); return; } if (response.closed() && !response.ended()) { builder.append(EMPTY_RESULT); return; } builder.append(response.getStatusCode()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { Response response = finishEvent.getResponse(); if (null == response) { builder.append(EMPTY_RESULT); return; } builder.append(response.getStatusCode()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; public class InvocationContextAccessItem implements AccessLogItem { public static final String NOT_FOUND = "-"; final String varName; public InvocationContextAccessItem(String varName) { this.varName = varName; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { String invocationContextValue = getValueFromInvocationContext(accessLogEvent); if (StringUtils.isEmpty(invocationContextValue)) { builder.append(NOT_FOUND); return; } builder.append(invocationContextValue); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { Invocation invocation = finishEvent.getInvocation(); if (null == invocation || invocation.getContext() == null || StringUtils.isEmpty(finishEvent.getInvocation().getContext().get(varName))) { builder.append(NOT_FOUND); return; } builder.append(finishEvent.getInvocation().getContext().get(varName)); } protected String getValueFromInvocationContext(ServerAccessLogEvent accessLogEvent) { Map data = accessLogEvent.getRoutingContext().data(); if (null == data || null == data.get(RestConst.REST_INVOCATION_CONTEXT)) { return null; } return ((Invocation) data.get(RestConst.REST_INVOCATION_CONTEXT)).getContext(varName); } public String getVarName() { return varName; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class LocalHostAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append(accessLogEvent.getLocalAddress()); } /** * client do not need localhost */ @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() || null == restRequestImpl.getHttpClientRequest().connection() || null == restRequestImpl.getHttpClientRequest().connection().localAddress() || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().connection().localAddress().host())) { builder.append(EMPTY_RESULT); return; } builder.append(restRequestImpl.getHttpClientRequest().connection().localAddress().host()); } public static String getLocalAddress(RoutingContext context) { HttpServerRequest request = context.request(); if (null == request || null == request.localAddress() || StringUtils.isEmpty(request.localAddress().host())) { return EMPTY_RESULT; } return request.localAddress().host(); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class LocalPortAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || null == request.localAddress()) { builder.append(EMPTY_RESULT); return; } builder.append(request.localAddress().port()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() || null == restRequestImpl.getHttpClientRequest().connection() || null == restRequestImpl.getHttpClientRequest().connection().localAddress()) { builder.append(EMPTY_RESULT); return; } builder.append(restRequestImpl.getHttpClientRequest().connection().localAddress().port()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/PlainTextAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; /** * Print content as it is. */ public class PlainTextAccessItem implements AccessLogItem { private final String content; public PlainTextAccessItem(String content) { this.content = content; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append(content); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { builder.append(content); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class QueryStringAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || StringUtils.isEmpty(request.query())) { builder.append(EMPTY_RESULT); return; } builder.append(request.query()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().query())) { builder.append(EMPTY_RESULT); return; } builder.append(restRequestImpl.getHttpClientRequest().query()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class RemoteHostAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || null == request.remoteAddress() || StringUtils.isEmpty(request.remoteAddress().host())) { builder.append(EMPTY_RESULT); return; } builder.append(request.remoteAddress().host()); } @Override public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { Endpoint endpoint = clientLogEvent.getInvocation().getEndpoint(); if (null == endpoint || null == endpoint.getAddress() || StringUtils.isEmpty(((URIEndpointObject) endpoint.getAddress()).getHostOrIp())) { builder.append(EMPTY_RESULT); return; } builder.append(((URIEndpointObject) endpoint.getAddress()).getHostOrIp()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.MultiMap; import io.vertx.ext.web.RoutingContext; public class RequestHeaderAccessItem implements AccessLogItem { public static final String RESULT_NOT_FOUND = "-"; private final String varName; public RequestHeaderAccessItem(String varName) { this.varName = varName; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { MultiMap headers = accessLogEvent.getRoutingContext().request().headers(); if (null == headers || StringUtils.isEmpty(headers.get(varName))) { builder.append(RESULT_NOT_FOUND); return; } builder.append(headers.get(varName)); } @Override public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) clientLogEvent.getInvocation() .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() || null == restRequestImpl.getHttpClientRequest().headers() || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().headers().get(varName))) { builder.append(RESULT_NOT_FOUND); return; } builder.append(restRequestImpl.getHttpClientRequest().headers().get(varName)); } public String getVarName() { return varName; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpVersion; import io.vertx.ext.web.RoutingContext; public class RequestProtocolAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || null == request.version()) { builder.append(EMPTY_RESULT); return; } builder.append(getStringVersion(request.version())); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { Invocation invocation = finishEvent.getInvocation(); if (invocation == null || null == invocation.getEndpoint() || null == invocation.getEndpoint().getAddress() || !(invocation.getEndpoint().getAddress() instanceof URIEndpointObject) || !((URIEndpointObject) invocation.getEndpoint().getAddress()).isHttp2Enabled()) { builder.append("HTTP/1.1"); return; } builder.append("HTTP/2.0"); } private String getStringVersion(HttpVersion version) { switch (version) { case HTTP_2: return "HTTP/2.0"; case HTTP_1_0: return "HTTP/1.0"; case HTTP_1_1: return "HTTP/1.1"; default: return EMPTY_RESULT; } } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.swagger.invocation.Response; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class ResponseHeaderAccessItem implements AccessLogItem { public static final String RESULT_NOT_FOUND = "-"; private final String varName; public ResponseHeaderAccessItem(String varName) { this.varName = varName; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerResponse response = accessLogEvent.getRoutingContext().response(); if (null == response || null == response.headers() || StringUtils.isEmpty(response.headers().get(varName))) { builder.append(RESULT_NOT_FOUND); return; } builder.append(response.headers().get(varName)); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { Response response = finishEvent.getResponse(); String value = response != null ? response.getHeader(varName) : null; if (null == value) { builder.append(RESULT_NOT_FOUND); return; } builder.append(value); } public String getVarName() { return varName; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class ResponseSizeAccessItem implements AccessLogItem { // print zeroBytes when bytes is zero private final String zeroBytes; public ResponseSizeAccessItem(String zeroBytesPlaceholder) { zeroBytes = zeroBytesPlaceholder; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerResponse response = accessLogEvent.getRoutingContext().response(); if (null == response || 0 == response.bytesWritten()) { builder.append(zeroBytes); return; } builder.append(response.bytesWritten()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { // client do not know how to calculate is right, maybe Object#toString().length builder.append(zeroBytes); } public String getZeroBytes() { return zeroBytes; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; public class TraceIdAccessItem extends InvocationContextAccessItem { public static final String TRACE_ID = CoreConst.TRACE_ID_NAME; public TraceIdAccessItem() { super(TRACE_ID); } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { String traceId = getValueFromInvocationContext(accessLogEvent); if (StringUtils.isEmpty(traceId)) { traceId = accessLogEvent.getRoutingContext().request().getHeader(TRACE_ID); } builder.append(StringUtils.isEmpty(traceId) ? InvocationContextAccessItem.NOT_FOUND : traceId); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { Invocation invocation = finishEvent.getInvocation(); if (invocation == null || invocation.getContext() == null || StringUtils.isEmpty(invocation.getContext().get(TRACE_ID))) { builder.append(InvocationContextAccessItem.NOT_FOUND); return; } builder.append(invocation.getContext().get(TRACE_ID)); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; public class TransportAccessItem implements AccessLogItem { private static final String EMPTY_STR = "-"; /** * access log only support rest */ @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append("rest"); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { String transportName = finishEvent.getInvocation().getConfigTransportName(); if (!StringUtils.isEmpty(transportName)) { builder.append(transportName); return; } Endpoint endpoint = finishEvent.getInvocation().getEndpoint(); if (endpoint == null || StringUtils.isEmpty(endpoint.getEndpoint())) { builder.append(EMPTY_STR); return; } builder.append(endpoint.getEndpoint().split(":")[0]); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class UrlPathAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || StringUtils.isEmpty(request.path())) { builder.append(EMPTY_RESULT); return; } builder.append(request.path()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta(); SchemaMeta schemaMeta = finishEvent.getInvocation().getSchemaMeta(); if (operationMeta != null && schemaMeta != null && schemaMeta.getSwagger() != null) { builder.append(operationMeta.getOperationPath()); return; } RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().path())) { builder.append(EMPTY_RESULT); return; } builder.append(restRequestImpl.getHttpClientRequest().path()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryAccessItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.apache.servicecomb.common.rest.RestConst.REST_CLIENT_REQUEST_PATH; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class UrlPathWithQueryAccessItem implements AccessLogItem { public static final String EMPTY_RESULT = "-"; @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { HttpServerRequest request = accessLogEvent.getRoutingContext().request(); if (null == request || StringUtils.isEmpty(request.uri())) { builder.append(EMPTY_RESULT); return; } builder.append(request.uri()); } @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { Invocation invocation = finishEvent.getInvocation(); if (null == invocation || null == invocation.getLocalContext(REST_CLIENT_REQUEST_PATH) || StringUtils.isEmpty(invocation.getLocalContext(REST_CLIENT_REQUEST_PATH).toString())) { builder.append(EMPTY_RESULT); return; } builder.append(invocation.getLocalContext(REST_CLIENT_REQUEST_PATH).toString()); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.element.impl.CookieAccessItem; /** * The {@linkplain AccessLogItemCreator}s are able to instantiate a group of {@linkplain AccessLogItem}. */ public interface AccessLogItemCreator { /** * Create an instance of {@linkplain AccessLogItem} which is specified by the config. * @param config * e.g. For {@linkplain CookieAccessItem CookieItem}, * the pattern may be "%{varName}C", and it's config is "varName". Some {@linkplain AccessLogItem} with no configurable * pattern (like "%m") will receive {@code null} as config. */ AccessLogItem createItem(String config); } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser; public class AccessLogItemMeta { protected String prefix; protected String suffix; /** * Used for sorting {@linkplain AccessLogItemMeta}. Default value is 0. * Smaller one has higher priority. */ protected int order; protected AccessLogItemCreator accessLogItemCreator; public String getPrefix() { return prefix; } public AccessLogItemMeta setPrefix(String prefix) { this.prefix = prefix; return this; } public String getSuffix() { return suffix; } public AccessLogItemMeta setSuffix(String suffix) { this.suffix = suffix; return this; } public int getOrder() { return order; } public AccessLogItemMeta setOrder(int order) { this.order = order; return this; } public AccessLogItemCreator getAccessLogItemCreator() { return accessLogItemCreator; } public AccessLogItemMeta setAccessLogItemCreator(AccessLogItemCreator accessLogItemCreator) { this.accessLogItemCreator = accessLogItemCreator; return this; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogPatternParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser; import java.util.List; import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; /** * This parser will parse the rawPattern of access log and generate a list of {@link AccessLogItem}, * which will be used in {@link AccessLogGenerator} to generate * access log content. * @param the type of {@linkplain AccessLogItem * AccessLogParam.contextData}, which usually depends on the transport way. */ public interface AccessLogPatternParser { List> parsePattern(String rawPattern); } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/CompositeVertxRestAccessLogItemMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser; import java.util.List; /** * Hold a group of {@link VertxRestAccessLogItemMeta} so that user can define * only one VertxRestAccessLogItemMeta in spi loading file and load a group of meta. * * Once the access log loading mechanism finds that a meta is CompositeVertxRestAccessLogItemMeta, * the meta hold by it will be used in access log while this meta itself will be ignored. */ public abstract class CompositeVertxRestAccessLogItemMeta extends VertxRestAccessLogItemMeta { public abstract List getAccessLogItemMetas(); } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/VertxRestAccessLogItemMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser; import io.vertx.ext.web.RoutingContext; /** * For vertx-rest transport way. */ public class VertxRestAccessLogItemMeta extends AccessLogItemMeta { public VertxRestAccessLogItemMeta() { } public VertxRestAccessLogItemMeta(String prefix, String suffix, AccessLogItemCreator accessLogItemCreator, int order) { this.prefix = prefix; this.suffix = suffix; this.accessLogItemCreator = accessLogItemCreator; this.order = order; } public VertxRestAccessLogItemMeta(String prefix, AccessLogItemCreator accessLogItemCreator) { this(prefix, null, accessLogItemCreator, 0); } public VertxRestAccessLogItemMeta(String prefix, String suffix, AccessLogItemCreator accessLogItemCreator) { this(prefix, suffix, accessLogItemCreator, 0); } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser.impl; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.element.impl.CookieAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ConfigurableDatetimeAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.DurationMillisecondAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.DurationSecondAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.FirstLineOfRequestAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.HttpMethodAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.HttpStatusAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.InvocationContextAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.LocalHostAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.LocalPortAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.QueryStringAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RemoteHostAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RequestHeaderAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RequestProtocolAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseHeaderAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseSizeAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.TraceIdAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.TransportAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathWithQueryAccessItem; import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; import io.vertx.ext.web.RoutingContext; public class DefaultCompositeVertxRestAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta { private static final List SUPPORTED_META = new ArrayList<>(); static { final AccessLogItem httpMethodItem = new HttpMethodAccessItem(); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%m", config -> httpMethodItem)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-method", config -> httpMethodItem)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%s", config -> new HttpStatusAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("sc-status", config -> new HttpStatusAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%T", config -> new DurationSecondAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%D", config -> new DurationMillisecondAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%h", config -> new RemoteHostAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%v", config -> new LocalHostAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%p", config -> new LocalPortAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%B", config -> new ResponseSizeAccessItem("0"))); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%b", config -> new ResponseSizeAccessItem("-"))); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%r", config -> new FirstLineOfRequestAccessItem())); final AccessLogItem urlPathItem = new UrlPathAccessItem(); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%U", config -> urlPathItem)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri-stem", config -> urlPathItem)); final AccessLogItem queryStringItem = new QueryStringAccessItem(); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%q", config -> queryStringItem)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri-query", config -> queryStringItem)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri", config -> new UrlPathWithQueryAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%H", config -> new RequestProtocolAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%t", config -> new ConfigurableDatetimeAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%SCB-traceId", config -> new TraceIdAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%SCB-transport", config -> new TransportAccessItem())); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}t", ConfigurableDatetimeAccessItem::new)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}i", RequestHeaderAccessItem::new)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}o", ResponseHeaderAccessItem::new)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}C", CookieAccessItem::new)); SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}SCB-ctx", InvocationContextAccessItem::new)); } @Override public List getAccessLogItemMetas() { return SUPPORTED_META; } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser.impl; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.element.impl.PlainTextAccessItem; import org.apache.servicecomb.common.accessLog.core.parser.AccessLogItemMeta; import org.apache.servicecomb.common.accessLog.core.parser.AccessLogPatternParser; import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import io.vertx.ext.web.RoutingContext; /** * The parser is used for rest-over-vertx transport. */ public class VertxRestAccessLogPatternParser implements AccessLogPatternParser { private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestAccessLogPatternParser.class); public static final Comparator accessLogItemMetaComparator = (m1, m2) -> { int result = m1.getOrder() - m2.getOrder(); if (result != 0) { return result; } // one of m1 & m2 has suffix, but the other one doesn't have if (m1.getSuffix() == null ^ m2.getSuffix() == null) { return m1.getSuffix() == null ? 1 : -1; } if (null != m1.getSuffix()) { result = comparePlaceholderString(m1.getSuffix(), m2.getSuffix()); } return 0 == result ? comparePlaceholderString(m1.getPrefix(), m2.getPrefix()) : result; }; private final List metaList = new ArrayList<>(); public VertxRestAccessLogPatternParser() { List loadedMeta = loadVertxRestLogItemMeta(); if (null == loadedMeta || loadedMeta.isEmpty()) { LOGGER.error("cannot load AccessLogItemMeta!"); throw new IllegalStateException("cannot load AccessLogItemMeta!"); } for (VertxRestAccessLogItemMeta meta : loadedMeta) { if (CompositeVertxRestAccessLogItemMeta.class.isAssignableFrom(meta.getClass())) { this.metaList.addAll(((CompositeVertxRestAccessLogItemMeta) meta).getAccessLogItemMetas()); } else { this.metaList.add(meta); } } sortAccessLogItemMeta(this.metaList); } @VisibleForTesting List getMetaList() { return metaList; } @VisibleForTesting List loadVertxRestLogItemMeta() { return SPIServiceUtils.getOrLoadSortedService(VertxRestAccessLogItemMeta.class); } /** * Behavior of this compare: * 1. comparePlaceholderString("abc","bbc") < 0 * 2. comparePlaceholderString("abc","ab") < 0 * 3. comparePlaceholderString("abc","abc") = 0 */ public static int comparePlaceholderString(String s1, String s2) { int result = s1.compareTo(s2); if (0 == result) { return result; } // there are two possible cases: // 1. s1="ab", s2="def" // 2. s1="ab", s2="abc" // in the case1 just return the result, but int the case2 the result should be reversed return result < 0 ? (s2.startsWith(s1) ? -result : result) : (s1.startsWith(s2) ? -result : result); } /** * Sort all of the {@link AccessLogItemMeta}, the meta that is in front of the others has higher priority. * * Sort rule(priority decreased): *
    *
  1. compare the {@link AccessLogItemMeta#getOrder()}
  2. *
  3. compare the {@link AccessLogItemMeta#getSuffix()} in lexicographic order, if one's suffix is start with * the other one's suffix, this one(who's suffix is longer) has higher priority
  4. *
  5. compare the {@link AccessLogItemMeta#getPrefix()}, compare rule is the same as suffix.
  6. *
* * e.g. given a list of {@link AccessLogItemMeta} like below: *
    *
  1. (%ac{,}bcd)
  2. *
  3. (%ac{,}bc)
  4. *
  5. (%ac{,}a)
  6. *
  7. (%ac,)
  8. *
  9. (%b,)
  10. *
  11. (%a)
  12. *
  13. (%{,}b)
  14. *
  15. (%{,}bc)
  16. *
* the result is: *
    *
  1. (%ac{,}a)
  2. *
  3. (%ac{,}bcd)
  4. *
  5. (%ac{,}bc)
  6. *
  7. (%{,}bc)
  8. *
  9. (%{,}b)
  10. *
  11. (%ac,)
  12. *
  13. (%a)
  14. *
  15. (%b,)
  16. *
*/ public static void sortAccessLogItemMeta(List accessLogItemMetaList) { accessLogItemMetaList.sort(accessLogItemMetaComparator); } /** * @param rawPattern The access log pattern string specified by users. * @return A list of {@linkplain AccessLogItem} which actually generate the content of access log. */ @Override public List> parsePattern(String rawPattern) { LOGGER.info("parse the pattern of access log: [{}]", rawPattern); List locationList = matchAccessLogItem(rawPattern); locationList = fillInPlainTextLocation(rawPattern, locationList); return convertToItemList(rawPattern, locationList); } /** * Use the {@link #metaList} to match rawPattern. * Return a list of {@link AccessLogItemLocation}. * Plain text is ignored. */ private List matchAccessLogItem(String rawPattern) { List locationList = new ArrayList<>(); int cursor = 0; while (cursor < rawPattern.length()) { AccessLogItemLocation candidate = null; for (VertxRestAccessLogItemMeta meta : metaList) { if (null != candidate && null == meta.getSuffix()) { // if user define item("%{","}ab") and item("%{_","}abc") and the pattern is "%{_var}ab}abc" // currently the result is item("%{","_var","}ab"), plaintext("}abc") // is this acceptable? // We've gotten an AccessLogItem with suffix, so there is no need to match those without suffix, // just break this match loop cursor = candidate.tail; break; } if (rawPattern.startsWith(meta.getPrefix(), cursor)) { if (null == meta.getSuffix()) { // for simple type AccessLogItem, there is no need to try to match the next item. candidate = new AccessLogItemLocation(cursor, meta); cursor = candidate.tail; break; } // for configurable type, like %{...}i, more check is needed // e.g. "%{varName1}o ${varName2}i" should be divided into // ResponseHeaderItem with varName="varName1" and RequestHeaderItem with varName="varName2" // INSTEAD OF RequestHeaderItem with varName="varName1}o ${varName2" int rear = rawPattern.indexOf(meta.getSuffix(), cursor); if (rear < 0) { continue; } if (null == candidate || rear < candidate.suffixIndex) { candidate = new AccessLogItemLocation(cursor, rear, meta); } // There is a matched item which is in front of this item, so this item is ignored. } } if (candidate == null) { ++cursor; continue; } locationList.add(candidate); } return locationList; } /** * After processing of {@link #matchAccessLogItem(String)}, all of the placeholders of {@link AccessLogItem} have been * picked out. So the rest part of rawPattern should be treated as plain text. Those parts will be located in this * method and wrapped as {@link PlainTextAccessItem}. * @param rawPattern raw pattern string of access log * @param locationList locations picked out by {@link #matchAccessLogItem(String)} * @return all of the locations including {@link PlainTextAccessItem}. */ private List fillInPlainTextLocation(String rawPattern, List locationList) { List resultList = new ArrayList<>(); if (locationList.isEmpty()) { resultList.add(createTextPlainItemLocation(0, rawPattern.length())); return resultList; } Iterator itemLocationIterator = locationList.iterator(); AccessLogItemLocation previousItemLocation = itemLocationIterator.next(); if (previousItemLocation.prefixIndex > 0) { resultList.add(createTextPlainItemLocation(0, previousItemLocation.prefixIndex)); } resultList.add(previousItemLocation); while (itemLocationIterator.hasNext()) { AccessLogItemLocation thisItemLocation = itemLocationIterator.next(); if (previousItemLocation.tail < thisItemLocation.prefixIndex) { resultList.add(createTextPlainItemLocation(previousItemLocation.tail, thisItemLocation.prefixIndex)); } previousItemLocation = thisItemLocation; resultList.add(previousItemLocation); } if (previousItemLocation.tail < rawPattern.length()) { resultList.add(createTextPlainItemLocation( previousItemLocation.tail, rawPattern.length())); } return resultList; } private AccessLogItemLocation createTextPlainItemLocation(int front, int rear) { return new AccessLogItemLocation(front, rear); } private List> convertToItemList(String rawPattern, List locationList) { List> itemList = new ArrayList<>(); for (AccessLogItemLocation accessLogItemLocation : locationList) { VertxRestAccessLogItemMeta accessLogItemMeta = accessLogItemLocation.accessLogItemMeta; if (null == accessLogItemMeta) { // a PlainTextItem location itemList.add(new PlainTextAccessItem(rawPattern.substring( accessLogItemLocation.prefixIndex, accessLogItemLocation.tail ))); continue; } itemList.add(accessLogItemMeta.getAccessLogItemCreator().createItem( getConfigString(rawPattern, accessLogItemLocation)) ); } return itemList; } private String getConfigString(String rawPattern, AccessLogItemLocation accessLogItemLocation) { if (null == accessLogItemLocation.getSuffix()) { // simple AccessLogItem return null; } return rawPattern.substring( accessLogItemLocation.prefixIndex + accessLogItemLocation.getPrefix().length(), accessLogItemLocation.suffixIndex); } private static class AccessLogItemLocation { /** * prefixIndex = rawPattern.indexOf(prefix) */ final int prefixIndex; /** * suffixIndex = rawPattern.indexOf(suffix) */ final int suffixIndex; /** * tail = suffixIndex + suffix.length() */ final int tail; VertxRestAccessLogItemMeta accessLogItemMeta; /** * for {@link PlainTextAccessItem} only */ AccessLogItemLocation(int prefixIndex, int suffixIndex) { this.prefixIndex = prefixIndex; this.suffixIndex = suffixIndex; this.tail = suffixIndex; } /** * for configurable type AccessLogItem */ AccessLogItemLocation(int prefixIndex, int suffixIndex, VertxRestAccessLogItemMeta accessLogItemMeta) { this.prefixIndex = prefixIndex; this.suffixIndex = suffixIndex; this.tail = suffixIndex + accessLogItemMeta.getSuffix().length(); this.accessLogItemMeta = accessLogItemMeta; } /** * for simple type AccessLogItem */ AccessLogItemLocation(int prefixIndex, VertxRestAccessLogItemMeta accessLogItemMeta) { this.prefixIndex = prefixIndex; this.suffixIndex = prefixIndex + accessLogItemMeta.getPrefix().length(); this.tail = this.suffixIndex; this.accessLogItemMeta = accessLogItemMeta; } public String getPrefix() { if (null == accessLogItemMeta) { return null; } return accessLogItemMeta.getPrefix(); } public String getSuffix() { if (null == accessLogItemMeta) { return null; } return accessLogItemMeta.getSuffix(); } } } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/placeholder/AccessLogItemTypeEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.placeholder; /** * record what kinds of access log item we support */ public enum AccessLogItemTypeEnum { TEXT_PLAIN, // %m, cs-method HTTP_METHOD, // %s, sc-status HTTP_STATUS, // %T DURATION_IN_SECOND, // %D DURATION_IN_MILLISECOND, // %h REMOTE_HOSTNAME, // %v LOCAL_HOSTNAME, // %p LOCAL_PORT, // %B RESPONSE_SIZE, // %b RESPONSE_SIZE_CLF, // %r FIRST_LINE_OF_REQUEST, // %U, cs-uri-stem URL_PATH, // %q, cs-uri-query QUERY_STRING, // cs-uri URL_PATH_WITH_QUERY, //%H REQUEST_PROTOCOL, // %t DATETIME_DEFAULT, // %{PATTERN}t, %{PATTERN|TIMEZONE|LOCALE}t DATETIME_CONFIGURABLE, // %{VARNAME}i REQUEST_HEADER, // %{VARNAME}o RESPONSE_HEADER, // %{VARNAME}C COOKIE, // %SCB-traceId SCB_TRACE_ID, // %{VARNAME}SCB-ctx SCB_INVOCATION_CONTEXT } ================================================ FILE: common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.server; import org.apache.servicecomb.common.accessLog.AccessLogConfig; import org.apache.servicecomb.common.accessLog.AccessLogInitializer; import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class ServerDefaultInitializer implements AccessLogInitializer { private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); private AccessLogGenerator accessLogGenerator; @Override public void init(EventBus eventBus, AccessLogConfig accessLogConfig) { if (!accessLogConfig.isServerLogEnabled()) { return; } accessLogGenerator = new AccessLogGenerator(accessLogConfig.getServerLogPattern()); eventBus.register(this); } @Subscribe @AllowConcurrentEvents public void onRequestReceived(ServerAccessLogEvent accessLogEvent) { LOGGER.info(accessLogGenerator.generateServerLog(accessLogEvent)); } } ================================================ FILE: common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.AccessLogInitializer ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.common.accessLog.client.ClientDefaultInitializer org.apache.servicecomb.common.accessLog.server.ServerDefaultInitializer ================================================ FILE: common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.common.accessLog.core.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta ================================================ FILE: common/common-access-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.common.accessLog.AccessLogConfiguration ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core; import static org.mockito.Mockito.when; import java.text.SimpleDateFormat; import java.util.TimeZone; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ConfigurableDatetimeAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.HttpMethodAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.PlainTextAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RemoteHostAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.UserDefinedAccessLogItem; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class AccessLogGeneratorTest { private static final AccessLogGenerator LOG_GENERATOR = new AccessLogGenerator("%m - %t"); private static final AccessLogGenerator USER_DEFINED_LOG_GENERATOR = new AccessLogGenerator( "%h - - %{test-config}user-defined"); @Test public void testConstructor() { AccessLogItem[] elements = LOG_GENERATOR.getAccessLogItems(); Assertions.assertEquals(3, elements.length); Assertions.assertEquals(HttpMethodAccessItem.class, elements[0].getClass()); Assertions.assertEquals(PlainTextAccessItem.class, elements[1].getClass()); Assertions.assertEquals(ConfigurableDatetimeAccessItem.class, elements[2].getClass()); } @Test public void testServerLog() { RoutingContext context = Mockito.mock(RoutingContext.class); HttpServerRequest request = Mockito.mock(HttpServerRequest.class); long startMillisecond = 1416863450581L; ServerAccessLogEvent serverAccessLogEvent = new ServerAccessLogEvent(); serverAccessLogEvent.setMilliStartTime(startMillisecond).setRoutingContext(context); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, ConfigurableDatetimeAccessItem.DEFAULT_LOCALE); simpleDateFormat.setTimeZone(TimeZone.getDefault()); when(context.request()).thenReturn(request); when(request.method()).thenReturn(HttpMethod.DELETE); String log = LOG_GENERATOR.generateServerLog(serverAccessLogEvent); Assertions.assertEquals("DELETE" + " - " + simpleDateFormat.format(startMillisecond), log); } @Test public void testClientLog() { Invocation invocation = Mockito.mock(Invocation.class); InvocationStageTrace stageTrace = Mockito.mock(InvocationStageTrace.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); long startMillisecond = 1416863450581L; when(stageTrace.getStartInMillis()).thenReturn(startMillisecond); when(stageTrace.calcTotal()).thenReturn(0L); when(invocation.getOperationMeta()).thenReturn(operationMeta); when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, null); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, ConfigurableDatetimeAccessItem.DEFAULT_LOCALE); simpleDateFormat.setTimeZone(TimeZone.getDefault()); when(operationMeta.getHttpMethod()).thenReturn(HttpMethod.DELETE.toString()); String log = LOG_GENERATOR.generateClientLog(finishEvent); Assertions.assertEquals("DELETE" + " - " + simpleDateFormat.format(startMillisecond), log); } @Test public void testUserDefinedLogGenerator() { AccessLogItem[] elements = USER_DEFINED_LOG_GENERATOR.getAccessLogItems(); Assertions.assertEquals(3, elements.length); Assertions.assertEquals(RemoteHostAccessItem.class, elements[0].getClass()); Assertions.assertEquals(PlainTextAccessItem.class, elements[1].getClass()); Assertions.assertEquals(UserDefinedAccessLogItem.class, elements[2].getClass()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.impl.CookieImpl; import io.vertx.ext.web.RoutingContext; public class CookieItemTest { public static final String COOKIE_NAME = "cookieName"; public static final String COOKIE_VALUE = "cookieVALUE"; private static final CookieAccessItem ELEMENT = new CookieAccessItem(COOKIE_NAME); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext mockContext; private HttpServerRequest httpServerRequest; private Invocation invocation; private RestClientRequestParameters restClientRequest; @BeforeEach public void initStrBuilder() { mockContext = Mockito.mock(RoutingContext.class); httpServerRequest = Mockito.mock(HttpServerRequest.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); accessLogEvent = new ServerAccessLogEvent(); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { HashSet cookieSet = new HashSet<>(); CookieImpl cookie = new CookieImpl(COOKIE_NAME, COOKIE_VALUE); cookieSet.add(cookie); Mockito.when(mockContext.request()).thenReturn(httpServerRequest); Mockito.when(httpServerRequest.cookies()).thenReturn(cookieSet); accessLogEvent.setRoutingContext(mockContext); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(COOKIE_VALUE, strBuilder.toString()); } @Test public void clientFormattedElement() { Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); Map cookieMap = new HashMap<>(); cookieMap.put(COOKIE_NAME, COOKIE_VALUE); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); when(restClientRequest.getCookieMap()).thenReturn(cookieMap); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(COOKIE_VALUE, strBuilder.toString()); } @Test public void serverFormattedElementOnCookieCountIsZero() { HashSet cookieSet = new HashSet<>(); Mockito.when(mockContext.request()).thenReturn(httpServerRequest); Mockito.when(httpServerRequest.cookies()).thenReturn(cookieSet); accessLogEvent.setRoutingContext(mockContext); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnCookieCountIsZero() { Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); Map cookieMap = new HashMap<>(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); when(restClientRequest.getCookieMap()).thenReturn(cookieMap); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnCookieSetIsNull() { Mockito.when(mockContext.request()).thenReturn(httpServerRequest); Mockito.when(httpServerRequest.cookies()).thenReturn(null); accessLogEvent.setRoutingContext(mockContext); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnCookieSetIsNull() { Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); when(restClientRequest.getCookieMap()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnNotFound() { HashSet cookieSet = new HashSet<>(); CookieImpl cookie = new CookieImpl("anotherCookieName", COOKIE_VALUE); cookieSet.add(cookie); Mockito.when(mockContext.request()).thenReturn(httpServerRequest); Mockito.when(httpServerRequest.cookies()).thenReturn(cookieSet); accessLogEvent.setRoutingContext(mockContext); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnNotFound() { Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); Map cookieMap = new HashMap<>(); cookieMap.put("anotherCookieValue", COOKIE_VALUE); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); when(restClientRequest.getCookieMap()).thenReturn(cookieMap); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.text.SimpleDateFormat; import java.util.Locale; import java.util.TimeZone; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class DatetimeConfigurableItemTest { private static final long START_MILLISECOND = 1416863450581L; private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private Invocation invocation; private InvocationStageTrace invocationStageTrace; @BeforeEach public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); invocationStageTrace = Mockito.mock(InvocationStageTrace.class); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); when(invocationStageTrace.getStartInMillis()).thenReturn(START_MILLISECOND); when(invocationStageTrace.calcTotal()).thenReturn(0L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(START_MILLISECOND); strBuilder = new StringBuilder(); } @Test public void getFormattedElementOnNoTimezone() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( "yyyy/MM/dd zzz||zh-CN"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void clientFormattedElementOnNoTimezone() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( "yyyy/MM/dd zzz||zh-CN"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void serverFormattedElementOnNoLocale() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString()); } @Test public void clientFormattedElementOnNoLocale() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString()); } @Test public void serverFormattedElementOnNoConfig() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( "||"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void clientFormattedElementOnNoConfig() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( "||"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void serverConstructorWithNoArg() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern()); Assertions.assertEquals(Locale.US, element.getLocale()); Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void clientConstructorWithNoArg() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern()); Assertions.assertEquals(Locale.US, element.getLocale()); Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void serverConstructorWithNoSeparator() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem("yyyy/MM/dd HH:mm:ss zzz"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zzz", Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern()); Assertions.assertEquals(Locale.US, element.getLocale()); Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } @Test public void clientConstructorWithNoSeparator() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem("yyyy/MM/dd HH:mm:ss zzz"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zzz", Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern()); Assertions.assertEquals(Locale.US, element.getLocale()); Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class DurationMillisecondItemTest { public static final DurationMillisecondAccessItem ELEMENT = new DurationMillisecondAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private Invocation invocation; private InvocationStageTrace invocationStageTrace; @BeforeEach public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); invocationStageTrace = Mockito.mock(InvocationStageTrace.class); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); when(invocationStageTrace.calcTotal()).thenReturn(1000_000L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(1L); accessLogEvent.setMilliEndTime(2L); strBuilder = new StringBuilder(); } @Test public void testAppendFormattedElement() { ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); strBuilder = new StringBuilder(); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class DurationSecondItemTest { public static final DurationSecondAccessItem ELEMENT = new DurationSecondAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private Invocation invocation; private InvocationStageTrace invocationStageTrace; @BeforeEach public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); invocationStageTrace = Mockito.mock(InvocationStageTrace.class); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); when(invocationStageTrace.calcTotal()).thenReturn(1L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(1L); strBuilder = new StringBuilder(); } @Test public void serverFormattedElementOn999ms() { accessLogEvent.setMilliEndTime(1000L); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } @Test public void clientFormattedElementOn999ms() { when(invocationStageTrace.calcTotal()).thenReturn(0L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } @Test public void serverFormattedElementOn1000ms() { accessLogEvent.setMilliEndTime(1001L); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } @Test public void clientFormattedElementOn1000ms() { when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } @Test public void serverFormattedElementOn1001ms() { accessLogEvent.setMilliEndTime(1002L); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } @Test public void clientFormattedElementOn1001ms() { when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpVersion; import io.vertx.ext.web.RoutingContext; public class FirstLineOfRequestItemTest { public static final FirstLineOfRequestAccessItem ELEMENT = new FirstLineOfRequestAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext mockContext; private Invocation invocation; private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; private Endpoint endpoint; private URIEndpointObject urlEndpoint; @BeforeEach public void initStrBuilder() { mockContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); endpoint = Mockito.mock(Endpoint.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(urlEndpoint); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(mockContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { HttpServerRequest request = Mockito.mock(HttpServerRequest.class); String uri = "/test/uri"; when(mockContext.request()).thenReturn(request); when(request.method()).thenReturn(HttpMethod.DELETE); when(request.path()).thenReturn(uri); when(request.version()).thenReturn(HttpVersion.HTTP_1_1); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("\"DELETE " + uri + " HTTP/1.1\"", strBuilder.toString()); } @Test public void clientFormattedElement() { String uri = "/test/uri"; when(clientRequest.getMethod()).thenReturn(HttpMethod.DELETE); when(clientRequest.path()).thenReturn(uri); when(urlEndpoint.isHttp2Enabled()).thenReturn(true); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("\"DELETE " + uri + " HTTP/2.0\"", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class HttpMethodItemTest { private static final HttpMethodAccessItem ITEM = new HttpMethodAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; private Endpoint endpoint; private URIEndpointObject urlEndpoint; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); endpoint = Mockito.mock(Endpoint.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(urlEndpoint); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { HttpServerRequest request = Mockito.mock(HttpServerRequest.class); Mockito.when(routingContext.request()).thenReturn(request); Mockito.when(request.method()).thenReturn(HttpMethod.DELETE); accessLogEvent.setRoutingContext(routingContext); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("DELETE", strBuilder.toString()); } @Test public void clientFormattedElement() { when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.getMethod()).thenReturn(HttpMethod.DELETE); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("DELETE", strBuilder.toString()); } @Test public void serverFormattedElementOnRequestIsNull() { accessLogEvent.setRoutingContext(routingContext); Mockito.when(routingContext.request()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnMethodIsNull() { HttpServerRequest request = Mockito.mock(HttpServerRequest.class); accessLogEvent.setRoutingContext(routingContext); Mockito.when(routingContext.request()).thenReturn(request); Mockito.when(request.method()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnMethodIsNull() { when(clientRequest.getMethod()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class HttpStatusItemTest { private static final HttpStatusAccessItem STATUS_ELEMENT = new HttpStatusAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Response response; private HttpServerResponse serverResponse; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); response = Mockito.mock(Response.class); serverResponse = Mockito.mock(HttpServerResponse.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { int statusCode = 200; when(routingContext.response()).thenReturn(serverResponse); when(serverResponse.getStatusCode()).thenReturn(statusCode); STATUS_ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("200", strBuilder.toString()); } @Test public void clientFormattedElement() { int statusCode = 200; when(finishEvent.getResponse()).thenReturn(response); when(response.getStatusCode()).thenReturn(statusCode); STATUS_ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("200", strBuilder.toString()); } @Test public void serverFormattedElementOnResponseIsNull() { Mockito.when(routingContext.response()).thenReturn(null); STATUS_ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); Mockito.when(routingContext.response()).thenReturn(serverResponse); Mockito.when(serverResponse.closed()).thenReturn(true); Mockito.when(serverResponse.ended()).thenReturn(false); strBuilder = new StringBuilder(); STATUS_ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnResponseIsNull() { when(finishEvent.getResponse()).thenReturn(null); STATUS_ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.ext.web.RoutingContext; public class InvocationContextItemTest { public static final String INVOCATION_CONTEXT_KEY = "testKey"; public static final String INVOCATION_CONTEXT_VALUE = "testValue"; private static final InvocationContextAccessItem ITEM = new InvocationContextAccessItem(INVOCATION_CONTEXT_KEY); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverGetFormattedItem() { Map routingContextData = new HashMap<>(); when(routingContext.data()).thenReturn(routingContextData); routingContextData.put(RestConst.REST_INVOCATION_CONTEXT, invocation); when(invocation.getContext(INVOCATION_CONTEXT_KEY)).thenReturn(INVOCATION_CONTEXT_VALUE); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(INVOCATION_CONTEXT_VALUE)); } @Test public void clientGetFormattedItem() { Map context = new HashMap<>(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(context); context.put(INVOCATION_CONTEXT_KEY, INVOCATION_CONTEXT_VALUE); ITEM.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(INVOCATION_CONTEXT_VALUE)); } @Test public void serverGetFormattedItemOnInvocationContextValueNotFound() { Map routingContextData = new HashMap<>(); Invocation invocation = Mockito.mock(Invocation.class); when(routingContext.data()).thenReturn(routingContextData); routingContextData.put(RestConst.REST_INVOCATION_CONTEXT, invocation); when(invocation.getContext(INVOCATION_CONTEXT_KEY)).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } @Test public void clientGetFormattedItemOnInvocationContextValueNotFound() { Map context = new HashMap<>(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(context); context.put(INVOCATION_CONTEXT_KEY, null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } @Test public void serverGetFormattedItemOnInvocationNotFound() { Map routingContextData = new HashMap<>(); when(routingContext.data()).thenReturn(routingContextData); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } @Test public void clientGetFormattedItemOnInvocationContextNotFound() { Map context = new HashMap<>(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(context); ITEM.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } @Test public void testGetFormattedItemOnRoutingContextDataNotFound() { when(routingContext.data()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } @Test public void clientGetFormattedItemOnRoutingContextDataNotFound() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } @Test public void clientGetFormattedItemOnInvocationNotFound() { when(finishEvent.getInvocation()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpConnection; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.RoutingContext; public class LocalHostItemTest { public static final LocalHostAccessItem ELEMENT = new LocalHostAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private HttpServerRequest serverRequest; private SocketAddress socketAddress; private Invocation invocation; private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; private HttpConnection connection; @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); serverRequest = Mockito.mock(HttpServerRequest.class); socketAddress = Mockito.mock(SocketAddress.class); invocation = Mockito.mock(Invocation.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); connection = Mockito.mock(HttpConnection.class); Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void clientFormattedItem() { String localAddress = "192.168.0.1"; when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(localAddress); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(localAddress, strBuilder.toString()); } @Test public void serverFormattedItem() { String localAddress = "192.168.0.1"; accessLogEvent.setLocalAddress(localAddress); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(localAddress, strBuilder.toString()); } @Test public void getLocalAddress() { String localHost = "testHost"; Mockito.when(routingContext.request()).thenReturn(serverRequest); Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); Mockito.when(socketAddress.host()).thenReturn(localHost); String result = LocalHostAccessItem.getLocalAddress(routingContext); Assertions.assertEquals(localHost, result); } @Test public void serverLocalAddressOnRequestIsNull() { Mockito.when(routingContext.request()).thenReturn(null); String result = LocalHostAccessItem.getLocalAddress(routingContext); Assertions.assertEquals("-", result); } @Test public void clientLocalAddressOnRequestIsNull() { when(restClientRequest.getHttpClientRequest()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverLocalAddressOnLocalAddressIsNull() { Mockito.when(routingContext.request()).thenReturn(serverRequest); Mockito.when(serverRequest.localAddress()).thenReturn(null); String result = LocalHostAccessItem.getLocalAddress(routingContext); Assertions.assertEquals("-", result); } @Test public void clientLocalAddressOnLocalAddressIsNull() { when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverLocalAddressOnHostIsNull() { Mockito.when(routingContext.request()).thenReturn(serverRequest); Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); Mockito.when(socketAddress.host()).thenReturn(null); String result = LocalHostAccessItem.getLocalAddress(routingContext); Assertions.assertEquals("-", result); } @Test public void clientLocalAddressOnHostIsNull() { when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverLocalAddressIsEmpty() { String localHost = ""; Mockito.when(routingContext.request()).thenReturn(serverRequest); Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); Mockito.when(socketAddress.host()).thenReturn(localHost); String result = LocalHostAccessItem.getLocalAddress(routingContext); Assertions.assertEquals("-", result); } @Test public void clientLocalAddressIsEmpty() { when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(""); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpConnection; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.RoutingContext; public class LocalPortItemTest { public static final LocalPortAccessItem ELEMENT = new LocalPortAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private HttpServerRequest serverRequest; private SocketAddress socketAddress; private Invocation invocation; private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; private HttpConnection connection; @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); serverRequest = Mockito.mock(HttpServerRequest.class); socketAddress = Mockito.mock(SocketAddress.class); invocation = Mockito.mock(Invocation.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); connection = Mockito.mock(HttpConnection.class); Map handlerMap = new HashMap<>(); handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { Mockito.when(routingContext.request()).thenReturn(serverRequest); Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); Mockito.when(socketAddress.port()).thenReturn(8080); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("8080", strBuilder.toString()); } @Test public void clientFormattedElement() { when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.port()).thenReturn(8080); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("8080", strBuilder.toString()); } @Test public void serverFormattedElementOnRequestIsNull() { Mockito.when(routingContext.request()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { when(restClientRequest.getHttpClientRequest()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnLocalAddressIsNull() { Mockito.when(routingContext.request()).thenReturn(serverRequest); Mockito.when(serverRequest.localAddress()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnLocalAddressIsNull() { when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/PlainTextItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class PlainTextItemTest { private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private StringBuilder strBuilder; @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); finishEvent = Mockito.mock(InvocationFinishEvent.class); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { PlainTextAccessItem element = new PlainTextAccessItem("contentTest"); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("contentTest", strBuilder.toString()); } @Test public void clientFormattedElement() { PlainTextAccessItem element = new PlainTextAccessItem("contentTest"); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("contentTest", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class QueryStringItemTest { private static final QueryStringAccessItem ITEM = new QueryStringAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private RestClientRequestParameters restClientRequest; private HttpServerRequest serverRequest; private HttpClientRequest clientRequest; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { String query = "?status=up"; when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.query()).thenReturn(query); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(query, strBuilder.toString()); } @Test public void clientFormattedElement() { String query = "?status=up"; Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(query); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(query, strBuilder.toString()); } @Test public void serverFormattedElementOnRequestIsNull() { when(routingContext.request()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnQueryIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.query()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnQueryIsNull() { Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnQueryIsEmpty() { String query = ""; when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.query()).thenReturn(query); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnQueryIsEmpty() { String query = ""; Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(query); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.RoutingContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class RemoteHostItemTest { public static final RemoteHostAccessItem ELEMENT = new RemoteHostAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private HttpServerRequest serverRequest; private Endpoint endpoint; private URIEndpointObject uriEndpointObject; private SocketAddress socketAddress; @BeforeEach public void initStrBuilder() { routingContext = mock(RoutingContext.class); finishEvent = mock(InvocationFinishEvent.class); invocation = mock(Invocation.class); serverRequest = mock(HttpServerRequest.class); endpoint = mock(Endpoint.class); uriEndpointObject = mock(URIEndpointObject.class); socketAddress = mock(SocketAddress.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { String remoteHost = "remoteHost"; when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.remoteAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(remoteHost); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(remoteHost, strBuilder.toString()); } @Test public void clientFormattedElement() { String remoteHost = "remoteHost"; when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(uriEndpointObject); when(uriEndpointObject.getHostOrIp()).thenReturn(remoteHost); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(remoteHost, strBuilder.toString()); } @Test public void serverFormattedElementOnRequestIsNull() { when(routingContext.request()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnRemoteAddressIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.remoteAddress()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRemoteAddressIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnHostIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.remoteAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnHostIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(uriEndpointObject); when(uriEndpointObject.getHostOrIp()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnHostIsEmpty() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.remoteAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(""); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnHostIsEmpty() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(uriEndpointObject); when(uriEndpointObject.getHostOrIp()).thenReturn(""); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.impl.headers.HeadersMultiMap; import io.vertx.ext.web.RoutingContext; public class RequestHeaderItemTest { private static final String VAR_NAME = "varName"; private static final RequestHeaderAccessItem ELEMENT = new RequestHeaderAccessItem(VAR_NAME); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private RestClientRequestParameters restClientRequest; private HttpServerRequest serverRequest; private HttpClientRequest clientRequest; private MultiMap headers; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); headers = Mockito.mock(MultiMap.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String testValue = "testValue"; headers.add(VAR_NAME, testValue); when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.headers()).thenReturn(headers); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(testValue, strBuilder.toString()); Assertions.assertEquals(ELEMENT.getVarName(), VAR_NAME); } @Test public void clientFormattedElement() { Map handlerContext = new HashMap<>(); String testValue = "testValue"; handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.headers()).thenReturn(headers); when(headers.get(VAR_NAME)).thenReturn(testValue); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(testValue, strBuilder.toString()); } @Test public void serverFormattedElementIfHeaderIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.headers()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementIfHeaderIsNull() { Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.headers()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementIfNotFound() { HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String testValue = "testValue"; headers.add("anotherKey", testValue); when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.headers()).thenReturn(headers); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpVersion; import io.vertx.ext.web.RoutingContext; public class RequestProtocolItemTest { private static final RequestProtocolAccessItem ITEM = new RequestProtocolAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private HttpServerRequest serverRequest; private Endpoint endpoint; private URIEndpointObject urlEndpoint; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); endpoint = Mockito.mock(Endpoint.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.version()).thenReturn(HttpVersion.HTTP_1_1); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("HTTP/1.1", strBuilder.toString()); strBuilder = new StringBuilder(); when(serverRequest.version()).thenReturn(HttpVersion.HTTP_1_0); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("HTTP/1.0", strBuilder.toString()); strBuilder = new StringBuilder(); when(serverRequest.version()).thenReturn(HttpVersion.HTTP_2); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("HTTP/2.0", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(urlEndpoint); when(urlEndpoint.isHttp2Enabled()).thenReturn(true); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("HTTP/2.0", strBuilder.toString()); strBuilder = new StringBuilder(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(urlEndpoint); when(urlEndpoint.isHttp2Enabled()).thenReturn(false); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("HTTP/1.1", strBuilder.toString()); } @Test public void serverFormattedElementOnVersionIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.version()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnVersionIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("HTTP/1.1", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpServerResponse; import io.vertx.core.http.impl.headers.HeadersMultiMap; import io.vertx.ext.web.RoutingContext; public class ResponseHeaderItemTest { private static final String VAR_NAME = "varName"; private static final ResponseHeaderAccessItem ELEMENT = new ResponseHeaderAccessItem(VAR_NAME); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private HttpServerResponse serverResponse; private Response response; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); serverResponse = Mockito.mock(HttpServerResponse.class); response = Mockito.mock(Response.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String headerValue = "headerValue"; headers.add(VAR_NAME, headerValue); when(routingContext.response()).thenReturn(serverResponse); when(serverResponse.headers()).thenReturn(headers); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(headerValue, strBuilder.toString()); Assertions.assertEquals(ELEMENT.getVarName(), VAR_NAME); } @Test public void clientFormattedElement() { String headerValue = "headerValue"; response = Response.ok(null) .setHeader(VAR_NAME, headerValue); when(finishEvent.getResponse()).thenReturn(response); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(headerValue, strBuilder.toString()); Assertions.assertEquals(ELEMENT.getVarName(), VAR_NAME); } @Test public void serverFormattedElementOnHeadersIsNull() { when(routingContext.response()).thenReturn(serverResponse); when(serverResponse.headers()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnHeadersIsNull() { when(finishEvent.getResponse()).thenReturn(response); when(response.getHeaders()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnResponseIsNull() { when(routingContext.response()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnResponseIsNull() { when(finishEvent.getResponse()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnNotFound() { HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String headerValue = "headerValue"; headers.add("anotherHeader", headerValue); when(routingContext.response()).thenReturn(serverResponse); when(serverResponse.headers()).thenReturn(headers); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnNotFound() { String headerValue = "headerValue"; MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.set("anotherHeader", headerValue); when(finishEvent.getResponse()).thenReturn(response); when(response.getHeaders()).thenReturn(headers); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class ResponseSizeItemTest { private static final ResponseSizeAccessItem ELEMENT = new ResponseSizeAccessItem("0"); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private HttpServerResponse serverResponse; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); serverResponse = Mockito.mock(HttpServerResponse.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { long bytesWritten = 16L; when(routingContext.response()).thenReturn(serverResponse); when(serverResponse.bytesWritten()).thenReturn(bytesWritten); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(String.valueOf(bytesWritten), strBuilder.toString()); } @Test public void clientFormattedElement() { ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } @Test public void getFormattedElementOnResponseIsNull() { when(routingContext.response()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } @Test public void getFormattedElementOnBytesWrittenIsZero() { long bytesWritten = 0L; when(routingContext.response()).thenReturn(serverResponse); when(serverResponse.bytesWritten()).thenReturn(bytesWritten); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class TraceIdItemTest { private static final TraceIdAccessItem ELEMENT = new TraceIdAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private HttpServerRequest serverRequest; private Invocation invocation; private final Map clientContext = new HashMap<>(); @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); clientContext.clear(); } @Test public void serverGetFormattedElementFromInvocationContext() { Map data = new HashMap<>(); String traceIdTest = "traceIdTest"; when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(traceIdTest); when(routingContext.data()).thenReturn(data); data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); } @Test public void clientGetFormattedElementFromInvocationContext() { String traceIdTest = "traceIdTest"; clientContext.put(CoreConst.TRACE_ID_NAME, traceIdTest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(clientContext); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); } @Test public void serverGetFormattedElementFromRequestHeader() { Map data = new HashMap<>(); String traceIdTest = "traceIdTest"; when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(null); when(routingContext.data()).thenReturn(data); data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(traceIdTest); when(routingContext.request()).thenReturn(serverRequest); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); } @Test public void serverGetFormattedElementOnTraceIdNotFound() { Map data = new HashMap<>(); when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(""); when(routingContext.data()).thenReturn(data); data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); when(routingContext.request()).thenReturn(serverRequest); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); strBuilder = new StringBuilder(); when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } @Test public void clientGetFormattedElementOnTraceIdNotFound() { clientContext.put(CoreConst.TRACE_ID_NAME, null); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(clientContext); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } @Test public void serverGetFormattedElementOnInvocationContextIsNull() { when(routingContext.data()).thenReturn(null); when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } @Test public void clientGetFormattedElementOnInvocationContextIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getContext()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } @Test public void serverGetFormattedElementOnDataIsNull() { when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); when(routingContext.request()).thenReturn(serverRequest); when(routingContext.data()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); MatcherAssert.assertThat(strBuilder.toString(), is("-")); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TransportItemTest { private static final TransportAccessItem ITEM = new TransportAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private Invocation invocation; private Endpoint endpoint; @BeforeEach public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); endpoint = Mockito.mock(Endpoint.class); accessLogEvent = new ServerAccessLogEvent(); strBuilder = new StringBuilder(); } @Test public void clientFormattedElement() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getConfigTransportName()).thenReturn("rest"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("rest", strBuilder.toString()); strBuilder = new StringBuilder(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("rest", strBuilder.toString()); } @Test public void serverFormattedElement() { ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("rest", strBuilder.toString()); } @Test public void clientConfigTransportNameIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getConfigTransportName()).thenReturn(null); strBuilder = new StringBuilder(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("rest", strBuilder.toString()); } @Test public void clientConfigTransportNameIsEmpty() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getConfigTransportName()).thenReturn(""); strBuilder = new StringBuilder(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("rest", strBuilder.toString()); } @Test public void clientALLIsEmpty() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getConfigTransportName()).thenReturn(null); strBuilder = new StringBuilder(); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getEndpoint()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.swagger.v3.oas.models.OpenAPI; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class UrlPathItemTest { private static final UrlPathAccessItem ITEM = new UrlPathAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private HttpServerRequest serverRequest; private OperationMeta operationMeta; private SchemaMeta schemaMeta; private OpenAPI swagger; private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); operationMeta = Mockito.mock(OperationMeta.class); schemaMeta = Mockito.mock(SchemaMeta.class); swagger = Mockito.mock(OpenAPI.class); restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void clientFormattedElement() { String uri = "/test"; when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getOperationMeta()).thenReturn(operationMeta); when(invocation.getSchemaMeta()).thenReturn(schemaMeta); when(schemaMeta.getSwagger()).thenReturn(swagger); when(operationMeta.getOperationPath()).thenReturn("/test"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); strBuilder = new StringBuilder(); Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(invocation.getOperationMeta()).thenReturn(null); when(invocation.getSchemaMeta()).thenReturn(null); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.path()).thenReturn(uri); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); } @Test public void serverFormattedElement() { String uri = "/test"; when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.path()).thenReturn(uri); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); } @Test public void serverFormattedElementOnRequestIsNull() { when(routingContext.request()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnMethodIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.path()).thenReturn(null); ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnMethodIsNull() { Map handlerContext = new HashMap<>(); handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.path()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryItemTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import static org.mockito.Mockito.when; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; public class UrlPathWithQueryItemTest { public static final UrlPathWithQueryAccessItem ELEMENT = new UrlPathWithQueryAccessItem(); private StringBuilder strBuilder; private InvocationFinishEvent finishEvent; private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; private Invocation invocation; private HttpServerRequest serverRequest; @BeforeEach public void initStrBuilder() { accessLogEvent = new ServerAccessLogEvent(); routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); accessLogEvent.setRoutingContext(routingContext); strBuilder = new StringBuilder(); } @Test public void serverFormattedElement() { String uri = "uriTest"; when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.uri()).thenReturn(uri); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); } @Test public void clientFormattedElement() { String uri = "uriTest"; when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(uri); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); } @Test public void serverFormattedElementOnRequestIsNull() { when(routingContext.request()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnRequestIsNull() { when(finishEvent.getInvocation()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnUriIsNull() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.uri()).thenReturn(null); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnUriIsNull() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void serverFormattedElementOnUriIsEmpty() { when(routingContext.request()).thenReturn(serverRequest); when(serverRequest.uri()).thenReturn(""); ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @Test public void clientFormattedElementOnUriIsEmpty() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(""); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; /** * For access log extension test */ public class UserDefinedAccessLogItem implements AccessLogItem { private final String config; public UserDefinedAccessLogItem(String config) { this.config = config; } @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append("user-defined-") .append(config); } @Override public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { builder.append("user-defined-") .append(config); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItemLowPriority.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.element.impl; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import io.vertx.ext.web.RoutingContext; /** * For access log extension test, will be overridden by {@link RemoteHostAccessItem}("%h"), * and takes no effect. */ public class UserDefinedAccessLogItemLowPriority implements AccessLogItem { @Override public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { builder.append("OverriddenItem"); } @Override public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { builder.append("OverriddenItem"); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser.impl; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.common.accessLog.core.element.impl.UserDefinedAccessLogItem; import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; public class TestCompositeExtendedAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta { private static final List META_LIST = new ArrayList<>(); static { META_LIST.add(new VertxRestAccessLogItemMeta("%{", "}user-defined", UserDefinedAccessLogItem::new)); } @Override public List getAccessLogItemMetas() { return META_LIST; } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestSingleExtendedAccessLogItemMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser.impl; import org.apache.servicecomb.common.accessLog.core.element.impl.UserDefinedAccessLogItemLowPriority; import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; public class TestSingleExtendedAccessLogItemMeta extends VertxRestAccessLogItemMeta { public TestSingleExtendedAccessLogItemMeta() { super("%h", null, config -> new UserDefinedAccessLogItemLowPriority(), 1); } } ================================================ FILE: common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParserTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.accessLog.core.parser.impl; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.TimeZone; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.accessLog.core.element.impl.CookieAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ConfigurableDatetimeAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.DurationMillisecondAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.DurationSecondAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.FirstLineOfRequestAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.HttpMethodAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.HttpStatusAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.InvocationContextAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.LocalHostAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.LocalPortAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.QueryStringAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RemoteHostAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RequestHeaderAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.RequestProtocolAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseHeaderAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseSizeAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.TraceIdAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.TransportAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathAccessItem; import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathWithQueryAccessItem; import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.ext.web.RoutingContext; public class VertxRestAccessLogPatternParserTest { private static final String ROW_PATTERN = "[cs-method] %m %s%T%D%h%v%p%B%b%r%U%q" + "cs-uri-stemcs-uri-querycs-uri%H%t%{yyyy MM dd HH:mm:ss zzz}t" + "%{yyyy MM dd HH:mm:ss|GMT+0|en-US}t" + "%{incoming-header}i" + "%{outgoing-header}o" + "%{cookie}C" + "%SCB-traceId" + "%{ctx}SCB-ctx" + "%SCB-transport"; private static final VertxRestAccessLogPatternParser logPatternParser = new VertxRestAccessLogPatternParser(); private ServerAccessLogEvent accessLogEvent; private RoutingContext routingContext; @BeforeEach public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setRoutingContext(routingContext); } @Test public void testParsePatternFullTest() { List> result = logPatternParser.parsePattern(ROW_PATTERN); Assertions.assertEquals(29, result.size()); StringBuilder builder = new StringBuilder(); result.get(0).appendServerFormattedItem(accessLogEvent, builder); Assertions.assertEquals("[", builder.toString()); Assertions.assertEquals(HttpMethodAccessItem.class, result.get(1).getClass()); builder = new StringBuilder(); result.get(2).appendServerFormattedItem(accessLogEvent, builder); Assertions.assertEquals("] ", builder.toString()); Assertions.assertEquals(HttpMethodAccessItem.class, result.get(3).getClass()); builder = new StringBuilder(); result.get(4).appendServerFormattedItem(accessLogEvent, builder); Assertions.assertEquals(" ", builder.toString()); Assertions.assertEquals(HttpStatusAccessItem.class, result.get(5).getClass()); Assertions.assertEquals(DurationSecondAccessItem.class, result.get(6).getClass()); Assertions.assertEquals(DurationMillisecondAccessItem.class, result.get(7).getClass()); Assertions.assertEquals(RemoteHostAccessItem.class, result.get(8).getClass()); Assertions.assertEquals(LocalHostAccessItem.class, result.get(9).getClass()); Assertions.assertEquals(LocalPortAccessItem.class, result.get(10).getClass()); Assertions.assertEquals(ResponseSizeAccessItem.class, result.get(11).getClass()); Assertions.assertEquals("0", ((ResponseSizeAccessItem) result.get(11)).getZeroBytes()); Assertions.assertEquals(ResponseSizeAccessItem.class, result.get(12).getClass()); Assertions.assertEquals("-", ((ResponseSizeAccessItem) result.get(12)).getZeroBytes()); Assertions.assertEquals(FirstLineOfRequestAccessItem.class, result.get(13).getClass()); Assertions.assertEquals(UrlPathAccessItem.class, result.get(14).getClass()); Assertions.assertEquals(QueryStringAccessItem.class, result.get(15).getClass()); Assertions.assertEquals(UrlPathAccessItem.class, result.get(16).getClass()); Assertions.assertEquals(QueryStringAccessItem.class, result.get(17).getClass()); Assertions.assertEquals(UrlPathWithQueryAccessItem.class, result.get(18).getClass()); Assertions.assertEquals(RequestProtocolAccessItem.class, result.get(19).getClass()); Assertions.assertEquals(ConfigurableDatetimeAccessItem.class, result.get(20).getClass()); Assertions.assertEquals(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, ((ConfigurableDatetimeAccessItem) result.get(20)).getPattern()); Assertions.assertEquals(ConfigurableDatetimeAccessItem.DEFAULT_LOCALE, ((ConfigurableDatetimeAccessItem) result.get(20)).getLocale()); Assertions.assertEquals(TimeZone.getDefault(), ((ConfigurableDatetimeAccessItem) result.get(20)).getTimezone()); Assertions.assertEquals("yyyy MM dd HH:mm:ss zzz", ((ConfigurableDatetimeAccessItem) result.get(21)).getPattern()); Assertions.assertEquals(ConfigurableDatetimeAccessItem.DEFAULT_LOCALE, ((ConfigurableDatetimeAccessItem) result.get(21)).getLocale()); Assertions.assertEquals(TimeZone.getDefault(), ((ConfigurableDatetimeAccessItem) result.get(21)).getTimezone()); Assertions.assertEquals("yyyy MM dd HH:mm:ss", ((ConfigurableDatetimeAccessItem) result.get(22)).getPattern()); Assertions.assertEquals(Locale.forLanguageTag("en-US"), ((ConfigurableDatetimeAccessItem) result.get(22)).getLocale()); Assertions.assertEquals(TimeZone.getTimeZone("GMT+0"), ((ConfigurableDatetimeAccessItem) result.get(22)).getTimezone()); Assertions.assertEquals(RequestHeaderAccessItem.class, result.get(23).getClass()); Assertions.assertEquals("incoming-header", ((RequestHeaderAccessItem) result.get(23)).getVarName()); Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(24).getClass()); Assertions.assertEquals("outgoing-header", ((ResponseHeaderAccessItem) result.get(24)).getVarName()); Assertions.assertEquals(CookieAccessItem.class, result.get(25).getClass()); Assertions.assertEquals("cookie", ((CookieAccessItem) result.get(25)).getVarName()); Assertions.assertEquals(TraceIdAccessItem.class, result.get(26).getClass()); Assertions.assertEquals(InvocationContextAccessItem.class, result.get(27).getClass()); Assertions.assertEquals("ctx", ((InvocationContextAccessItem) result.get(27)).getVarName()); Assertions.assertEquals(TransportAccessItem.class, result.get(28).getClass()); } @Test public void testParsePattern() { String pattern = " %m cs-uri-stem %{response-header}o "; List> result = logPatternParser.parsePattern(pattern); Assertions.assertEquals(7, result.size()); StringBuilder stringBuilder = new StringBuilder(); result.get(0).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); Assertions.assertEquals(HttpMethodAccessItem.class, result.get(1).getClass()); stringBuilder = new StringBuilder(); result.get(2).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); Assertions.assertEquals(UrlPathAccessItem.class, result.get(3).getClass()); stringBuilder = new StringBuilder(); result.get(4).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(5).getClass()); Assertions.assertEquals("response-header", ((ResponseHeaderAccessItem) result.get(5)).getVarName()); stringBuilder = new StringBuilder(); result.get(6).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); } @Test public void testParsePatternWithNoBlank() { String pattern = "%mcs-uri-stem%{response-header}o"; List> result = logPatternParser.parsePattern(pattern); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(HttpMethodAccessItem.class, result.get(0).getClass()); Assertions.assertEquals(UrlPathAccessItem.class, result.get(1).getClass()); Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(2).getClass()); Assertions.assertEquals("response-header", ((ResponseHeaderAccessItem) result.get(2)).getVarName()); } @Test public void testParsePatternComplex() { String pattern = "%m cs-uri-stem %{response-header}o abc cs-uri-query %s%{request} header}i plain cs-uri"; List> result = logPatternParser.parsePattern(pattern); Assertions.assertEquals(12, result.size()); Assertions.assertEquals(HttpMethodAccessItem.class, result.get(0).getClass()); StringBuilder stringBuilder = new StringBuilder(); result.get(1).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); Assertions.assertEquals(UrlPathAccessItem.class, result.get(2).getClass()); stringBuilder = new StringBuilder(); result.get(3).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(4).getClass()); Assertions.assertEquals("response-header", ((ResponseHeaderAccessItem) result.get(4)).getVarName()); stringBuilder = new StringBuilder(); result.get(5).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" abc ", stringBuilder.toString()); Assertions.assertEquals(QueryStringAccessItem.class, result.get(6).getClass()); stringBuilder = new StringBuilder(); result.get(7).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" ", stringBuilder.toString()); Assertions.assertEquals(HttpStatusAccessItem.class, result.get(8).getClass()); Assertions.assertEquals(RequestHeaderAccessItem.class, result.get(9).getClass()); Assertions.assertEquals("request} header", ((RequestHeaderAccessItem) result.get(9)).getVarName()); stringBuilder = new StringBuilder(); result.get(10).appendServerFormattedItem(accessLogEvent, stringBuilder); Assertions.assertEquals(" plain ", stringBuilder.toString()); Assertions.assertEquals(UrlPathWithQueryAccessItem.class, result.get(11).getClass()); } final Comparator comparator = VertxRestAccessLogPatternParser.accessLogItemMetaComparator; /** * one factor test */ @Test public void testCompareMetaSimple() { Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta(null, null, null, 0), new VertxRestAccessLogItemMeta(null, null, null, 1) ) < 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta(null, "}abc", null, 0), new VertxRestAccessLogItemMeta(null, null, null, 0) ) < 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta(null, "}abc", null, 0), new VertxRestAccessLogItemMeta(null, "}de", null, 0) ) < 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta(null, "}abc", null, 0), new VertxRestAccessLogItemMeta(null, "}ab", null, 0) ) < 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta("%abc", null, null, 0), new VertxRestAccessLogItemMeta("%de", null, null, 0) ) < 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta("%abc", null, null, 0), new VertxRestAccessLogItemMeta("%ab", null, null, 0) ) < 0 ); Assertions.assertEquals(0, comparator.compare( new VertxRestAccessLogItemMeta("%abc", null, null, 0), new VertxRestAccessLogItemMeta("%abc", null, null, 0) )); } /** * multiple factors test */ @Test public void testCompareMetaComplex() { Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta("%bcd", "}ab", null, 0), new VertxRestAccessLogItemMeta("%abc", "}abc", null, 0) ) > 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta("%abc", null, null, 0), new VertxRestAccessLogItemMeta("%bcd", "}ab", null, 0) ) > 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta("%bcd", "}abc", null, 0), new VertxRestAccessLogItemMeta("%abc", "}abc", null, 0) ) > 0 ); Assertions.assertTrue( comparator.compare( new VertxRestAccessLogItemMeta("%abc", "}abc", null, 1), new VertxRestAccessLogItemMeta("%ab", "}ab", null, 0) ) > 0 ); } @Test public void testComparePlaceholderString() { Assertions.assertTrue( VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "bbc") < 0 ); Assertions.assertTrue( VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "ab") < 0 ); Assertions.assertEquals(0, VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "abc")); Assertions.assertTrue( VertxRestAccessLogPatternParser.comparePlaceholderString("bbc", "abc") > 0 ); Assertions.assertTrue( VertxRestAccessLogPatternParser.comparePlaceholderString("ab", "abc") > 0 ); } @Test public void testExtendedVertxRestAccessLogItemCreator() { final List metaList0 = new ArrayList<>(); metaList0.add(new VertxRestAccessLogItemMeta("%{", "}abc", null)); metaList0.add(new VertxRestAccessLogItemMeta("%{", "}a", null)); metaList0.add(new VertxRestAccessLogItemMeta("%_", null, null, -1)); final List metaList1 = new ArrayList<>(); metaList0.add(new VertxRestAccessLogItemMeta("%a", "}abc", null)); metaList0.add(new VertxRestAccessLogItemMeta("%0", "}abc", null, 1)); metaList0.add(new VertxRestAccessLogItemMeta("%m", null, null)); CompositeVertxRestAccessLogItemMeta compositeMeta0 = new CompositeVertxRestAccessLogItemMeta() { @Override public List getAccessLogItemMetas() { return metaList0; } }; CompositeVertxRestAccessLogItemMeta compositeMeta1 = new CompositeVertxRestAccessLogItemMeta() { @Override public List getAccessLogItemMetas() { return metaList1; } }; List metaList = new ArrayList<>(1); metaList.add(compositeMeta0); metaList.add(compositeMeta1); metaList.add(new VertxRestAccessLogItemMeta("%{", null, null)); VertxRestAccessLogPatternParser parser = Mockito.mock(VertxRestAccessLogPatternParser.class); Mockito.when(parser.getMetaList()).thenAnswer(invocation -> { List resultMetaList = new ArrayList<>(); for (VertxRestAccessLogItemMeta meta : metaList) { if (CompositeVertxRestAccessLogItemMeta.class.isAssignableFrom(meta.getClass())) { resultMetaList.addAll(((CompositeVertxRestAccessLogItemMeta) meta).getAccessLogItemMetas()); } else { resultMetaList.add(meta); } } VertxRestAccessLogPatternParser.sortAccessLogItemMeta(resultMetaList); return resultMetaList; }); List accessLogItemMetaList = parser.getMetaList(); Assertions.assertEquals(7, accessLogItemMetaList.size()); Assertions.assertEquals("%_", accessLogItemMetaList.get(0).getPrefix()); Assertions.assertEquals("%a", accessLogItemMetaList.get(1).getPrefix()); Assertions.assertEquals("}abc", accessLogItemMetaList.get(1).getSuffix()); Assertions.assertEquals("%{", accessLogItemMetaList.get(2).getPrefix()); Assertions.assertEquals("}abc", accessLogItemMetaList.get(2).getSuffix()); Assertions.assertEquals("%{", accessLogItemMetaList.get(3).getPrefix()); Assertions.assertEquals("}a", accessLogItemMetaList.get(3).getSuffix()); Assertions.assertEquals("%m", accessLogItemMetaList.get(4).getPrefix()); Assertions.assertNull(accessLogItemMetaList.get(4).getSuffix()); Assertions.assertEquals("%{", accessLogItemMetaList.get(5).getPrefix()); Assertions.assertNull(accessLogItemMetaList.get(5).getSuffix()); Assertions.assertEquals("%0", accessLogItemMetaList.get(6).getPrefix()); Assertions.assertEquals("}abc", accessLogItemMetaList.get(6).getSuffix()); } } ================================================ FILE: common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.common.accessLog.core.parser.impl.TestCompositeExtendedAccessLogItemMeta org.apache.servicecomb.common.accessLog.core.parser.impl.TestSingleExtendedAccessLogItemMeta ================================================ FILE: common/common-protobuf/pom.xml ================================================ 4.0.0 org.apache.servicecomb common 3.4.0-SNAPSHOT common-protobuf Java Chassis::Common::Protobuf org.apache.servicecomb java-chassis-core org.apache.servicecomb foundation-protobuf com.google.protobuf protobuf-java test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb swagger-generator-springmvc test org.apache.servicecomb foundation-test-scaffolding org.xolstice.maven.plugins protobuf-maven-plugin ${protobuf-maven-plugin.version} com.google.protobuf:protoc:${protoc3-maven-plugin.version}:exe:${os.detected.classifier} true grpc-java io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc-java-plugin.version}:exe:${os.detected.classifier} generate-test-sources test-compile ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/HighwayJsonUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Date; import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; /** * Add a separate converter for highway. Now highway compile types depends on swagger information, do not use any consumer information, * This will cause any invocation will convert swagger raw types to consumer types. This will lose performance. * * In later improvements, need add consumer information to OperationProtobuf. * * Why not using RestObjectMapper? Because the reason above, highway use java.util.Date for date-time, and will serialize to a String * that can not convert to LocalDateTime. (This is a jackson change to format java.util.Date to `2017-07-21T16:32:28.320+0000` * but jackson can only convert `2017-07-21T16:32:28.320Z` to LocalDateTime */ public class HighwayJsonUtils { public static final ObjectMapper OBJ_MAPPER = new RestObjectMapper(); private HighwayJsonUtils() { } @SuppressWarnings("unchecked") public static T convertValue(Object fromValue, JavaType toValueType) { if (fromValue == null) { return null; } if (TypeFactory.defaultInstance().constructType(LocalDateTime.class).equals(toValueType)) { // jackson do not have a proper converter for this. if (fromValue instanceof Date) { return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(((Date) fromValue).getTime()), ZoneOffset.UTC); } } return OBJ_MAPPER.convertValue(fromValue, toValueType); } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.lang.reflect.Type; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.compiler.model.Message; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status.Family; public final class OperationProtobuf { private RequestRootSerializer requestRootSerializer; private RequestRootDeserializer requestRootDeserializer; private ResponseRootSerializer responseRootSerializer; private ResponseRootDeserializer responseRootDeserializer; private ResponseRootSerializer anyResponseRootSerializer; private ResponseRootDeserializer anyResponseRootDeserializer; public OperationProtobuf(ScopedProtobufSchemaManager scopedProtobufSchemaManager, Invocation invocation) { initRequestCodec(scopedProtobufSchemaManager, invocation); initResponseCodec(scopedProtobufSchemaManager, invocation); } public RequestRootSerializer getRequestRootSerializer() { return this.requestRootSerializer; } public RequestRootDeserializer getRequestRootDeserializer() { return this.requestRootDeserializer; } public ResponseRootSerializer findResponseRootSerializer(int statusCode) { if (Family.SUCCESSFUL.equals(Family.familyOf(statusCode))) { return this.responseRootSerializer; } return anyResponseRootSerializer; } public ResponseRootDeserializer findResponseRootDeserializer(int statusCode) { if (Family.SUCCESSFUL.equals(Family.familyOf(statusCode))) { return this.responseRootDeserializer; } return anyResponseRootDeserializer; } private void initProducerRequestCodec(Invocation invocation, Message requestMessage, ProtoMapper mapper) { Map swaggerParameterTypes = invocation.getOperationMeta().getSwaggerProducerOperation() .getSwaggerParameterTypes(); if (ProtoUtils.isWrapArguments(requestMessage)) { requestRootDeserializer = new RequestRootDeserializer<>( mapper.createRootDeserializer(requestMessage, swaggerParameterTypes), true, null); } else { if (swaggerParameterTypes.isEmpty()) { requestRootDeserializer = new RequestRootDeserializer<>( mapper.createRootDeserializer(requestMessage, Object.class), false, null); } else if (swaggerParameterTypes.size() == 1) { Entry entry = swaggerParameterTypes.entrySet().iterator().next(); requestRootDeserializer = new RequestRootDeserializer<>(mapper.createRootDeserializer(requestMessage, entry.getValue()), false, entry.getKey()); } else { throw new IllegalStateException( "unexpected operation definition " + invocation.getOperationMeta().getMicroserviceQualifiedName()); } } } private void initConsumerRequestCodec(Invocation invocation, Message requestMessage, ProtoMapper mapper) { if (ProtoUtils.isWrapArguments(requestMessage)) { requestRootSerializer = new RequestRootSerializer( mapper.createRootSerializer(requestMessage, Object.class), true, false); } else { if (invocation.getOperationMeta().parameterCount() == 0) { requestRootSerializer = new RequestRootSerializer(mapper.createRootSerializer(requestMessage, Object.class), false, false); } else if (invocation.getOperationMeta().parameterCount() == 1) { requestRootSerializer = new RequestRootSerializer(mapper.createRootSerializer(requestMessage, Object.class), false, true); } else { throw new IllegalStateException( "unexpected operation definition " + invocation.getOperationMeta().getMicroserviceQualifiedName()); } } } private void initRequestCodec(ScopedProtobufSchemaManager scopedProtobufSchemaManager, Invocation invocation) { ProtoMapper mapper = scopedProtobufSchemaManager.getOrCreateProtoMapper(invocation.getSchemaMeta()); Message requestMessage = mapper.getRequestMessage(invocation.getOperationMeta().getOperationId()); if (invocation.isProducer()) { initProducerRequestCodec(invocation, requestMessage, mapper); } else { initConsumerRequestCodec(invocation, requestMessage, mapper); } } private void initProviderResponseCode(Message responseMessage, ProtoMapper mapper, JavaType responseType) { if (ProtoUtils.isWrapProperty(responseMessage)) { responseRootSerializer = new ResponseRootSerializer( mapper.createRootSerializer(responseMessage, responseType), true, false); } else { if (ProtoUtils.isEmptyMessage(responseMessage)) { responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, Object.class), false, false); } else { responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, responseType), false, true); } } } private void initConsumerResponseCode(Message responseMessage, ProtoMapper mapper, JavaType responseType) { if (ProtoUtils.isWrapProperty(responseMessage)) { responseRootSerializer = new ResponseRootSerializer( mapper.createRootSerializer(responseMessage, responseType), true, false); responseRootDeserializer = new ResponseRootDeserializer<>( mapper.createRootDeserializer(responseMessage, responseType), false); } else { if (ProtoUtils.isEmptyMessage(responseMessage)) { responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, Object.class), false, false); responseRootDeserializer = new ResponseRootDeserializer<>( mapper.createRootDeserializer(responseMessage, Object.class), true); } else { responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, responseType), false, false); responseRootDeserializer = new ResponseRootDeserializer<>( mapper.createRootDeserializer(responseMessage, responseType), false); } } } private void initResponseCodec(ScopedProtobufSchemaManager scopedProtobufSchemaManager, Invocation invocation) { ProtoMapper mapper = scopedProtobufSchemaManager.getOrCreateProtoMapper(invocation.getSchemaMeta()); Message responseMessage = mapper.getResponseMessage(invocation.getOperationMeta().getOperationId()); JavaType responseType = invocation.findResponseType(Status.OK.getStatusCode()); if (invocation.isProducer()) { initProviderResponseCode(responseMessage, mapper, responseType); } else { initConsumerResponseCode(responseMessage, mapper, responseType); } anyResponseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(ProtoConst.ANY, Object.class), false, true); anyResponseRootDeserializer = new ResponseRootDeserializer<>( mapper.createRootDeserializer(ProtoConst.ANY, Object.class), false); } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ProtobufManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.util.HashMap; import java.util.Map; import java.util.Objects; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.swagger.invocation.InvocationType; import com.fasterxml.jackson.databind.JavaType; import com.google.common.annotations.VisibleForTesting; public final class ProtobufManager { public static final String EXT_ID = "protobuf"; private static final Object LOCK = new Object(); static class RuntimeCacheKey { final InvocationType invocationType; final String uniqueOperationId; // Using response type as the cache key. // May consider request type as well, but now not implemented final JavaType responseType; public RuntimeCacheKey(InvocationType invocationType, String operationId, JavaType responseType) { this.invocationType = invocationType; this.uniqueOperationId = operationId; this.responseType = responseType; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RuntimeCacheKey that = (RuntimeCacheKey) o; if (invocationType != that.invocationType) { return false; } if (!uniqueOperationId.equals(that.uniqueOperationId)) { return false; } return Objects.equals(responseType, that.responseType); } @Override public int hashCode() { int result = invocationType.hashCode(); result = 31 * result + uniqueOperationId.hashCode(); result = 31 * result + (responseType != null ? responseType.hashCode() : 0); return result; } } private static final Map RUNTIME_CACHE = new HashMap<>(); public static OperationProtobuf getOrCreateOperation(Invocation invocation) { RuntimeCacheKey cacheKey = new RuntimeCacheKey(invocation.getInvocationType(), invocation.getOperationMeta().getMicroserviceQualifiedName(), invocation.findResponseType(Status.OK.getStatusCode())); OperationProtobuf operationProtobuf = RUNTIME_CACHE.get(cacheKey); if (operationProtobuf == null) { synchronized (LOCK) { MicroserviceMeta microserviceMeta = invocation.getMicroserviceMeta(); ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); if (scopedProtobufSchemaManager == null) { scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); } operationProtobuf = RUNTIME_CACHE.get(cacheKey); if (operationProtobuf == null) { operationProtobuf = new OperationProtobuf(scopedProtobufSchemaManager, invocation); RUNTIME_CACHE.put(cacheKey, operationProtobuf); } } } return operationProtobuf; } @VisibleForTesting public static void clear() { RUNTIME_CACHE.clear(); } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootDeserializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; public class RequestRootDeserializer { private final boolean wrapArgument; private final String parameterName; private final RootDeserializer rootDeserializer; public RequestRootDeserializer(RootDeserializer rootDeserializer, boolean wrapArgument, String parameterName) { this.rootDeserializer = rootDeserializer; this.wrapArgument = wrapArgument; this.parameterName = parameterName; } @SuppressWarnings("unchecked") public Map deserialize(byte[] bytes) throws IOException { if (!wrapArgument) { Map result = new HashMap<>(1); Object obj = rootDeserializer.deserialize(bytes); if (obj instanceof PropertyWrapper) { obj = ((PropertyWrapper) obj).getValue(); } if (parameterName == null) { return result; } result.put(parameterName, obj); return result; } else { return (Map) rootDeserializer.deserialize(bytes); } } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.io.IOException; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import io.vertx.core.json.JsonObject; public class RequestRootSerializer { private final RootSerializer rootSerializer; private final boolean noTypesInfo; private final boolean isWrap; public RequestRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { this.rootSerializer = serializer; this.noTypesInfo = noTypesInfo; this.isWrap = isWrap; } @SuppressWarnings("unchecked") public byte[] serialize(Object value) throws IOException { if (noTypesInfo && !isWrap) { Object param = ((Map) value).values().iterator().next(); if (param instanceof JsonObject) { param = ((JsonObject) param).getMap(); } return this.rootSerializer.serialize(param); } else { return this.rootSerializer.serialize(value); } } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootDeserializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.io.IOException; import org.apache.commons.lang3.ClassUtils; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import com.fasterxml.jackson.databind.JavaType; public class ResponseRootDeserializer { private final RootDeserializer rootDeserializer; private final boolean empty; public ResponseRootDeserializer(RootDeserializer rootDeserializer, boolean empty) { this.rootDeserializer = rootDeserializer; this.empty = empty; } @SuppressWarnings("unchecked") public T deserialize(byte[] bytes, JavaType invocationTimeType) throws IOException { if (empty) { rootDeserializer.deserialize(bytes); // read buffers if possible. return null; } Object obj = rootDeserializer.deserialize(bytes); if (obj instanceof PropertyWrapper) { obj = ((PropertyWrapper) obj).getValue(); } if (needConvert(obj, invocationTimeType)) { obj = HighwayJsonUtils.convertValue(obj, invocationTimeType); } return (T) obj; } public static boolean needConvert(Object obj, JavaType invocationTimeType) { if (obj == null || ClassUtils.isPrimitiveOrWrapper(obj.getClass()) || invocationTimeType.isPrimitive() || ProtoConst.OBJECT_TYPE.equals(invocationTimeType)) { return false; } if (obj.getClass() == invocationTimeType.getRawClass()) { return false; } if (invocationTimeType.getRawClass().isAssignableFrom(obj.getClass())) { if (invocationTimeType.getContentType() == null) { return false; } } return true; } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.RootSerializer; public class ResponseRootSerializer { private final RootSerializer rootSerializer; private final boolean noTypesInfo; private final boolean isWrap; public ResponseRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { this.rootSerializer = serializer; this.noTypesInfo = noTypesInfo; this.isWrap = isWrap; } public byte[] serialize(Object value) throws IOException { if (noTypesInfo && !isWrap) { return this.rootSerializer.serialize(value); } else { Map responseValue = new HashMap<>(1); // key is fixed to "value" in IDL responseValue.put("value", value); return this.rootSerializer.serialize(responseValue); } } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoMethod.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.ws.rs.core.Response.Status; public class ProtoMethod { private String argTypeName; @JsonProperty // key is status private final Map responses = new HashMap<>(); private ProtoResponse defaultResponse; public String getArgTypeName() { return argTypeName; } public void setArgTypeName(String argTypeName) { this.argTypeName = argTypeName; } public void addResponse(String status, ProtoResponse response) { if (status.equals("default")) { defaultResponse = response; return; } int statusCode = Integer.parseInt(status); responses.put(statusCode, response); if (defaultResponse == null && statusCode == Status.OK.getStatusCode()) { defaultResponse = response; } } public ProtoResponse findResponse(int statusCode) { ProtoResponse response = responses.get(statusCode); if (response != null) { return response; } if (statusCode == Status.OK.getStatusCode()) { for (Entry code : responses.entrySet()) { if (HttpStatus.isSuccess(code.getKey())) { return responses.get(code.getKey()); } } } if (HttpStatus.isSuccess(statusCode)) { return responses.get(Status.OK.getStatusCode()); } return defaultResponse; } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; public class ProtoResponse { private String typeName; public String getTypeName() { return typeName; } public void setTypeName(String typeName) { this.typeName = typeName; } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoToStringGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import java.util.List; import com.google.common.base.Strings; import io.protostuff.compiler.model.Enum; import io.protostuff.compiler.model.EnumConstant; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Import; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.Proto; import io.protostuff.compiler.model.Service; import io.protostuff.compiler.model.ServiceMethod; public class ProtoToStringGenerator { private final Proto proto; public ProtoToStringGenerator(Proto proto) { this.proto = proto; } public String protoToString() { StringBuilder sb = new StringBuilder(); appendLine(sb, "syntax = \"%s\";", proto.getSyntax()); for (Import importValue : proto.getImports()) { appendLine(sb, "import \"%s\";", importValue.getValue()); } appendLine(sb, "package %s;\n", proto.getPackage().getValue()); for (Message message : proto.getMessages()) { messageToString(message, sb); } for (Enum enumValue : proto.getEnums()) { enumToString(enumValue, sb); } for (Service service : proto.getServices()) { serviceToString(service, sb); } return sb.toString(); } private void serviceToString(Service service, StringBuilder sb) { appendLine(sb, "service %s {", service.getName()); for (ServiceMethod serviceMethod : service.getMethods()) { commentsToString(serviceMethod.getCommentLines(), sb, 2); appendLine(sb, " rpc %s (%s) returns (%s);\n", serviceMethod.getName(), serviceMethod.getArgTypeName(), serviceMethod.getReturnTypeName()); } if (!service.getMethods().isEmpty()) { sb.setLength(sb.length() - 1); } appendLine(sb, "}"); } protected void enumToString(Enum enumValue, StringBuilder sb) { appendLine(sb, "enum %s {", enumValue.getName()); for (EnumConstant enumConstant : enumValue.getConstants()) { appendLine(sb, " %s = %s;", enumConstant.getName(), enumConstant.getValue()); } sb.append("}\n\n"); } private void commentsToString(List comments, StringBuilder sb, int padLeft) { if (comments.isEmpty()) { return; } String pad = Strings.repeat(" ", padLeft) + "//"; for (String comment : comments) { sb.append(pad); appendLine(sb, comment); } } private void messageToString(Message message, StringBuilder sb) { commentsToString(message.getCommentLines(), sb, 0); appendLine(sb, "message %s {", message.getName()); for (Field field : message.getFields()) { sb.append(" "); fieldToString(field, field.isRepeated(), sb); } appendLine(sb, "}\n"); } private void fieldToString(Field field, boolean repeated, StringBuilder sb) { if (field.isMap()) { fieldMapToString(field, sb); return; } if (repeated) { fieldRepeatedToString(field, sb); return; } appendLine(sb, "%s %s = %d;", field.getTypeName(), field.getName(), field.getTag()); } private void fieldRepeatedToString(Field field, StringBuilder sb) { sb.append("repeated "); fieldToString(field, false, sb); } private void fieldMapToString(Field field, StringBuilder sb) { Message entryMessage = (Message) field.getType(); Field keyField = entryMessage.getField(1); Field valueField = entryMessage.getField(2); // map name = 1; appendLine(sb, "map<%s, %s> %s = %d;", keyField.getTypeName(), valueField.getTypeName(), field.getName(), field.getTag()); } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SchemaSwaggerTypeAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import java.util.List; import io.swagger.v3.oas.models.media.Schema; public class SchemaSwaggerTypeAdapter implements SwaggerTypeAdapter { private final Schema schema; public SchemaSwaggerTypeAdapter(Schema schema) { this.schema = schema; } @Override public String getRefType() { return schema.get$ref(); } @Override public Schema getArrayItem() { return schema.getItems(); } @Override public Schema getMapItem() { return (Schema) schema.getAdditionalProperties(); } @Override @SuppressWarnings("unchecked") public List getEnum() { return (List) schema.getEnum(); } @Override public String getType() { return schema.getType(); } @Override public String getFormat() { return schema.getFormat(); } @Override public boolean isJavaLangObject() { return "object".equals(getType()); } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.hash.Hashing; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.Proto; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import io.vertx.core.json.Json; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; @SuppressWarnings({"rawtypes", "unchecked"}) public class SwaggerToProtoGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerToProtoGenerator.class); private final String protoPackage; private final OpenAPI swagger; private final StringBuilder msgStringBuilder = new StringBuilder(); private final StringBuilder serviceBuilder = new StringBuilder(); private final Set imports = new HashSet<>(); private final Set messages = new HashSet<>(); private List pending = new ArrayList<>(); // not java package // better to be: app_${app}.mid_{microservice}.sid_{schemaId} public SwaggerToProtoGenerator(String protoPackage, OpenAPI swagger) { this.protoPackage = escapePackageName(protoPackage); this.swagger = swagger; } public Proto convert() { convertDefinitions(); convertOperations(); do { List oldPending = pending; pending = new ArrayList<>(); for (Runnable runnable : oldPending) { runnable.run(); } } while (!pending.isEmpty()); return createProto(); } public static String escapePackageName(String name) { return name.replaceAll("[\\-:]", "_"); } public static String escapeMessageName(String name) { return name.replaceAll("\\.", "_"); } public static boolean isValidEnum(String name) { return !name.contains(".") && !name.contains("-"); } private void convertDefinitions() { if (swagger.getComponents() == null || swagger.getComponents().getSchemas() == null) { return; } for (Entry entry : swagger.getComponents().getSchemas().entrySet()) { convertDefinition(entry.getKey(), entry.getValue()); } } @SuppressWarnings("unchecked") private void convertDefinition(String modelName, Schema model) { Map properties = model.getProperties(); if (properties == null) { // it's a empty message properties = Collections.emptyMap(); } createMessage(modelName, properties); } private void createMessage(String protoName, Map properties, String... annotations) { if (!messages.add(protoName)) { // already created return; } for (String annotation : annotations) { msgStringBuilder.append("//"); appendLine(msgStringBuilder, annotation); } appendLine(msgStringBuilder, "message %s {", protoName); int tag = 1; for (Entry entry : properties.entrySet()) { Schema property = entry.getValue(); String propertyType = convertSwaggerType(property); appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); tag++; } appendLine(msgStringBuilder, "}"); } private void addImports(Proto proto) { imports.add(proto.getFilename()); for (Message message : proto.getMessages()) { messages.add(message.getCanonicalName()); } } private String convertSwaggerType(Object swaggerType) { if (swaggerType == null) { // void addImports(ProtoConst.EMPTY_PROTO); return ProtoConst.EMPTY.getCanonicalName(); } SwaggerTypeAdapter adapter = SwaggerTypeAdapter.create(swaggerType); String type = tryFindEnumType(adapter.getEnum()); if (type != null) { return type; } type = findBaseType(adapter.getType(), adapter.getFormat()); if (type != null) { return type; } type = adapter.getRefType(); if (type != null) { return type.substring(Components.COMPONENTS_SCHEMAS_REF.length()); } Schema itemProperty = adapter.getArrayItem(); if (itemProperty != null) { return "repeated " + convertArrayOrMapItem(itemProperty); } itemProperty = adapter.getMapItem(); if (itemProperty != null) { return String.format("map", convertArrayOrMapItem(itemProperty)); } if (adapter.isJavaLangObject()) { addImports(ProtoConst.ANY_PROTO); return ProtoConst.ANY.getCanonicalName(); } throw new IllegalStateException(String .format("not support swagger type, class=%s, content=%s.", swaggerType.getClass().getName(), Json.encode(swaggerType))); } private String convertArrayOrMapItem(Schema itemProperty) { SwaggerTypeAdapter itemAdapter = SwaggerTypeAdapter.create(itemProperty); // List>, need to wrap if (itemAdapter.getArrayItem() != null) { String protoName = generateWrapPropertyName(List.class.getSimpleName(), itemAdapter.getArrayItem()); pending.add(() -> wrapPropertyToMessage(protoName, itemProperty)); return protoName; } // List>, need to wrap if (itemAdapter.getMapItem() != null) { String protoName = generateWrapPropertyName(Map.class.getSimpleName(), itemAdapter.getMapItem()); pending.add(() -> wrapPropertyToMessage(protoName, itemProperty)); return protoName; } return convertSwaggerType(itemProperty); } private String generateWrapPropertyName(String prefix, Schema property) { SwaggerTypeAdapter adapter = SwaggerTypeAdapter.create(property); // List>, need to wrap if (adapter.getArrayItem() != null) { return generateWrapPropertyName(prefix + List.class.getSimpleName(), adapter.getArrayItem()); } // List>, need to wrap if (adapter.getMapItem() != null) { return generateWrapPropertyName(prefix + Map.class.getSimpleName(), adapter.getMapItem()); } // message name cannot have . (package separator) return prefix + StringUtils.capitalize(escapeMessageName(convertSwaggerType(adapter))); } private void wrapPropertyToMessage(String protoName, Schema property) { createMessage(protoName, Collections.singletonMap("value", property), ProtoConst.ANNOTATION_WRAP_PROPERTY); } private String tryFindEnumType(List enums) { if (enums != null && !enums.isEmpty()) { String strEnums = enums.toString(); String enumName = "Enum_" + Hashing.sha256().hashString(strEnums, StandardCharsets.UTF_8); pending.add(() -> createEnum(enumName, enums)); return enumName; } return null; } private void createEnum(String enumName, List enums) { if (!messages.add(enumName)) { // already created return; } appendLine(msgStringBuilder, "enum %s {", enumName); for (int idx = 0; idx < enums.size(); idx++) { if (isValidEnum(enums.get(idx))) { appendLine(msgStringBuilder, " %s =%d;", enums.get(idx), idx); } else { throw new IllegalStateException( String.format("enum class [%s] name [%s] not supported by protobuffer.", enumName, enums.get(idx))); } } appendLine(msgStringBuilder, "}"); } private String findBaseType(String swaggerType, String swaggerFmt) { String key = swaggerType + ":" + swaggerFmt; return switch (key) { case "boolean:null" -> "bool"; case "integer:int32" -> "sint32"; case "integer:int64" -> "sint64"; case "integer:null" -> "string"; // BigInteger like values case "number:double" -> "double"; case "number:float" -> "float"; case "number:null" -> "string"; // BigDecimal like values case "string:null" -> "string"; case "string:byte" -> "bytes"; case "string:date" -> "int64"; case "string:date-time" -> "int64"; case "string:binary" -> throw new IllegalArgumentException("proto buffer not support file upload/download"); default -> null; }; } private void convertOperations() { Paths paths = swagger.getPaths(); if (paths == null || paths.isEmpty()) { return; } appendLine(serviceBuilder, "service MainService {"); for (PathItem path : paths.values()) { for (Operation operation : path.readOperations()) { if (isUpload(operation)) { LOGGER.warn("Not support operation for highway {}.{}, {}", this.protoPackage, operation.getOperationId(), "file upload not supported"); continue; } else if (isDownload(operation)) { LOGGER.warn("Not support operation for highway {}.{}, {}", this.protoPackage, operation.getOperationId(), "file download not supported"); continue; } try { convertOperation(operation); } catch (Exception e) { LOGGER.error("Not support operation for highway {}.{}", this.protoPackage, operation.getOperationId(), e); } } } serviceBuilder.setLength(serviceBuilder.length() - 1); appendLine(serviceBuilder, "}"); } private boolean isUpload(Operation operation) { return operation.getRequestBody() != null && operation.getRequestBody().getContent() != null && operation.getRequestBody().getContent().get(MediaType.MULTIPART_FORM_DATA) != null; } private boolean isDownload(Operation operation) { return operation.getResponses().get(SwaggerConst.SUCCESS_KEY) != null && operation.getResponses().get(SwaggerConst.SUCCESS_KEY).getContent() != null && operation.getResponses().get(SwaggerConst.SUCCESS_KEY).getContent().get(MediaType.MULTIPART_FORM_DATA) != null; } private void convertOperation(Operation operation) { ProtoMethod protoMethod = new ProtoMethod(); fillRequestType(operation, protoMethod); fillResponseType(operation, protoMethod); appendLine(serviceBuilder, " //%s%s", ProtoConst.ANNOTATION_RPC, Json.encode(protoMethod)); appendLine(serviceBuilder, " rpc %s (%s) returns (%s);\n", operation.getOperationId(), protoMethod.getArgTypeName(), protoMethod.findResponse(Status.OK.getStatusCode()).getTypeName()); } private void fillRequestType(Operation operation, ProtoMethod protoMethod) { int parametersCount = parametersCount(operation); if (parametersCount == 0) { addImports(ProtoConst.EMPTY_PROTO); protoMethod.setArgTypeName(ProtoConst.EMPTY.getCanonicalName()); return; } if (parametersCount == 1) { String type = convertSwaggerType(oneSchema(operation)); if (messages.contains(type)) { protoMethod.setArgTypeName(type); return; } } String wrapName = StringUtils.capitalize(operation.getOperationId()) + "RequestWrap"; createWrapArgs(wrapName, wrapSchema(operation)); protoMethod.setArgTypeName(wrapName); } private Map wrapSchema(Operation operation) { Map properties = new LinkedHashMap<>(); if (operation.getParameters() != null) { for (Parameter parameter : operation.getParameters()) { properties.put(parameter.getName(), parameter.getSchema()); } } if (operation.getRequestBody() != null && operation.getRequestBody().getContent().size() != 0) { if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties() .forEach((k, v) -> properties.put((String) k, (Schema) v)); } else { properties.put((String) operation.getRequestBody().getExtensions().get(SwaggerConst.EXT_BODY_NAME), operation.getRequestBody().getContent().get( operation.getRequestBody().getContent().keySet().iterator().next()).getSchema()); } } return properties; } private Schema oneSchema(Operation operation) { if (operation.getParameters() != null && operation.getParameters().size() == 1) { return operation.getParameters().get(0).getSchema(); } if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { return (Schema) operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema() .getProperties() .values().iterator().next(); } return operation.getRequestBody().getContent().get( operation.getRequestBody().getContent().keySet().iterator().next()).getSchema(); } private int parametersCount(Operation operation) { int parameters = operation.getParameters() == null ? 0 : operation.getParameters().size(); if (operation.getRequestBody() != null) { if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { parameters = parameters + operation.getRequestBody() .getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().size(); } else if (operation.getRequestBody().getContent().size() != 0) { parameters = parameters + 1; } } return parameters; } private void fillResponseType(Operation operation, ProtoMethod protoMethod) { for (Entry entry : operation.getResponses().entrySet()) { Schema schema = null; if (entry.getValue().getContent() != null && entry.getValue().getContent().size() != 0) { schema = entry.getValue().getContent().get( entry.getValue().getContent().keySet().iterator().next()).getSchema(); } String type = convertSwaggerType(schema); boolean wrapped = !messages.contains(type); ProtoResponse protoResponse = new ProtoResponse(); protoResponse.setTypeName(type); if (wrapped) { String wrapName = StringUtils.capitalize(operation.getOperationId()) + "ResponseWrap" + entry.getKey(); wrapPropertyToMessage(wrapName, schema); protoResponse.setTypeName(wrapName); } protoMethod.addResponse(entry.getKey(), protoResponse); } } private void createWrapArgs(String wrapName, Map properties) { createMessage(wrapName, properties, ProtoConst.ANNOTATION_WRAP_ARGUMENTS); } protected Proto createProto() { StringBuilder sb = new StringBuilder(); appendLine(sb, "syntax = \"proto3\";"); for (String importMsg : imports) { appendLine(sb, "import \"%s\";", importMsg); } if (StringUtils.isNotEmpty(protoPackage)) { sb.append("package ").append(protoPackage).append(";\n"); } sb.append(msgStringBuilder); sb.append(serviceBuilder); ProtoParser protoParser = new ProtoParser(); return protoParser.parseFromContent(sb.toString()); } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerTypeAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import java.util.List; import io.swagger.v3.oas.models.media.Schema; public interface SwaggerTypeAdapter { static SwaggerTypeAdapter create(Object swaggerType) { if (swaggerType instanceof SwaggerTypeAdapter) { return (SwaggerTypeAdapter) swaggerType; } if (swaggerType instanceof Schema) { return new SchemaSwaggerTypeAdapter((Schema) swaggerType); } throw new IllegalStateException("not support swagger type: " + swaggerType.getClass().getName()); } String getRefType(); Schema getArrayItem(); Schema getMapItem(); List getEnum(); String getType(); String getFormat(); boolean isJavaLangObject(); } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; import org.apache.servicecomb.swagger.SwaggerUtils; import com.google.common.hash.Hashing; import io.protostuff.compiler.model.Proto; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings({"rawtypes", "unchecked"}) public class SchemaToProtoGenerator { record IdentifierRunnable(Schema identifier, Runnable target) implements Runnable { @Override public void run() { this.target.run(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } IdentifierRunnable that = (IdentifierRunnable) o; return SwaggerUtils.schemaEquals(identifier, that.identifier); } @Override public int hashCode() { return SwaggerUtils.schemaHashCode(identifier); } } private final String protoPackage; private final OpenAPI openAPI; private final Schema rootSchema; private final String rootName; private final Set messages = new HashSet<>(); private final StringBuilder msgStringBuilder = new StringBuilder(); private List pending = new ArrayList<>(); public SchemaToProtoGenerator(String protoPackage, OpenAPI openAPI, Schema rootSchema, String rootName) { this.protoPackage = protoPackage; this.openAPI = openAPI; this.rootSchema = rootSchema; this.rootName = rootName; } public Proto convert() { createMessage(this.rootSchema); int iteration = 0; do { List oldPending = pending; pending = new ArrayList<>(); for (Runnable runnable : oldPending) { runnable.run(); } if (pending.size() >= oldPending.size()) { iteration++; } } while (!pending.isEmpty() && iteration < 1000); if (iteration == 1000) { throw new IllegalArgumentException( String.format("Failed to create schema %s. May be cyclic object.", this.rootName)); } Map wrap = new HashMap<>(1); wrap.put("value", this.rootSchema); createMessage(rootName, wrap, ProtoConst.ANNOTATION_WRAP_PROPERTY); return createProto(); } protected Proto createProto() { StringBuilder sb = new StringBuilder(); appendLine(sb, "syntax = \"proto3\";"); if (StringUtils.isNotEmpty(protoPackage)) { sb.append("package ").append(protoPackage).append(";\n"); } sb.append(msgStringBuilder); ProtoParser protoParser = new ProtoParser(); return protoParser.parseFromContent(sb.toString()); } private String findSchemaType(Schema schema) { @SuppressWarnings("unchecked") String type = tryFindEnumType((List) schema.getEnum()); if (type != null) { return type; } type = findBaseType(schema.getType(), schema.getFormat()); if (type != null) { return type; } Schema itemProperty = schema.getItems(); if (itemProperty != null) { String containerType = findArrayOrMapItemType(itemProperty); if (containerType != null) { return "repeated " + containerType; } return null; } itemProperty = (Schema) schema.getAdditionalProperties(); if (itemProperty != null) { String containerType = findArrayOrMapItemType(itemProperty); if (containerType != null) { return String.format("map", containerType); } return null; } type = schema.get$ref(); if (type != null) { String typeName = type.substring(Components.COMPONENTS_SCHEMAS_REF.length()); Schema refSchema = openAPI.getComponents().getSchemas().get(typeName); if (refSchema == null) { throw new IllegalArgumentException("not found ref in components " + type); } if (StringUtils.isEmpty(refSchema.getName())) { refSchema.setName(typeName); } return findSchemaType(refSchema); } return findObjectType(schema); } private String findObjectType(Schema schema) { String name = schema.getName(); if (messages.contains(name)) { return name; } return null; } private void createEnum(String enumName, List enums) { if (!messages.add(enumName)) { // already created return; } appendLine(msgStringBuilder, "enum %s {", enumName); for (int idx = 0; idx < enums.size(); idx++) { if (isValidEnum(enums.get(idx))) { appendLine(msgStringBuilder, " %s =%d;", enums.get(idx), idx); } else { throw new IllegalStateException( String.format("enum class [%s] name [%s] not supported by protobuffer.", enumName, enums.get(idx))); } } appendLine(msgStringBuilder, "}"); } public static boolean isValidEnum(String name) { return !name.contains(".") && !name.contains("-"); } private String tryFindEnumType(List enums) { if (enums != null && !enums.isEmpty()) { String strEnums = enums.toString(); String enumName = "Enum_" + Hashing.sha256().hashString(strEnums, StandardCharsets.UTF_8); pending.add(() -> createEnum(enumName, enums)); return enumName; } return null; } private String findBaseType(String swaggerType, String swaggerFmt) { String key = swaggerType + ":" + swaggerFmt; return switch (key) { case "boolean:null" -> "bool"; case "integer:int32" -> "sint32"; case "integer:int64" -> "sint64"; case "integer:null" -> "string"; // BigInteger like values case "number:double" -> "double"; case "number:float" -> "float"; case "number:null" -> "string"; // BigDecimal like values case "string:null" -> "string"; case "string:byte" -> "bytes"; case "string:date" -> "int64"; case "string:date-time" -> "int64"; case "string:binary" -> throw new IllegalArgumentException("proto buffer not support file upload/download"); default -> null; }; } private String findArrayOrMapItemType(Schema itemProperty) { // List>, need to wrap if (itemProperty.getItems() != null) { return findWrapPropertyType(List.class.getSimpleName(), itemProperty.getItems()); } // List>, need to wrap if (itemProperty.getAdditionalProperties() != null) { return findWrapPropertyType(Map.class.getSimpleName(), (Schema) itemProperty.getAdditionalProperties()); } return findSchemaType(itemProperty); } private String findWrapPropertyType(String prefix, Schema property) { // List>, need to wrap if (property.getItems() != null) { return findWrapPropertyType(prefix + List.class.getSimpleName(), property.getItems()); } // List>, need to wrap if (property.getAdditionalProperties() != null) { return findWrapPropertyType(prefix + Map.class.getSimpleName(), (Schema) property.getAdditionalProperties()); } String type = findSchemaType(property); if (type == null) { return null; } // message name cannot have . (package separator) return prefix + StringUtils.capitalize(escapeMessageName(type)); } public static String escapeMessageName(String name) { return name.replaceAll("\\.", "_"); } private void wrapPropertyToMessage(String protoName, Schema property) { createMessage(protoName, Collections.singletonMap("value", property), ProtoConst.ANNOTATION_WRAP_PROPERTY); } private void createMessage(String protoName, Map properties, String... annotations) { for (String annotation : annotations) { msgStringBuilder.append("//"); appendLine(msgStringBuilder, annotation); } appendLine(msgStringBuilder, "message %s {", protoName); int tag = 1; for (Entry entry : properties.entrySet()) { Schema property = entry.getValue(); String propertyType = findSchemaType(property); appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); tag++; } appendLine(msgStringBuilder, "}"); } public void createMessage(Schema schema) { String ref = schema.get$ref(); if (ref != null) { String typeName = ref.substring(Components.COMPONENTS_SCHEMAS_REF.length()); Schema refSchema = openAPI.getComponents().getSchemas().get(typeName); if (refSchema == null) { throw new IllegalArgumentException("not found ref in components " + ref); } if (StringUtils.isEmpty(refSchema.getName())) { refSchema.setName(typeName); } createMessage(refSchema); return; } boolean wait = false; //array or map if (isArrayOrMap(schema)) { Schema mapOrArrayItem = arrayOrMapItem(schema); if (findSchemaType(mapOrArrayItem) == null) { createMessageTask(mapOrArrayItem); wait = true; } else { if (createMapOrArrayMessageTask(mapOrArrayItem, true, schema)) { wait = true; } } } //object if (schema.getProperties() != null) { for (Entry entry : schema.getProperties().entrySet()) { if (findSchemaType(entry.getValue()) == null) { createMessageTask(entry.getValue()); wait = true; } else if (isArrayOrMap(entry.getValue())) { if (createMapOrArrayMessageTask(arrayOrMapItem(entry.getValue()), false, null)) { wait = true; } } } } if (wait) { IdentifierRunnable runnable = new IdentifierRunnable(schema, () -> createMessage(schema)); if (!pending.contains(runnable)) { pending.add(runnable); } return; } if (findSchemaType(schema) != null) { return; } messages.add(schema.getName()); appendLine(msgStringBuilder, "message %s {", schema.getName()); List> sortedProperties = new ArrayList<>(schema.getProperties().entrySet()); sortedProperties.sort(Comparator.comparing(Map.Entry::getKey)); int tag = 1; for (Entry entry : sortedProperties) { Schema property = entry.getValue(); String propertyType = findSchemaType(property); appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); tag++; } appendLine(msgStringBuilder, "}"); } private boolean isArrayOrMap(Schema value) { return value.getItems() != null || value.getAdditionalProperties() != null; } private Schema arrayOrMapItem(Schema schema) { return schema.getItems() == null ? (Schema) schema.getAdditionalProperties() : schema.getItems(); } private void createMessageTask(Schema schema) { IdentifierRunnable runnable = new IdentifierRunnable(schema, () -> createMessage(schema)); if (!pending.contains(runnable)) { pending.add(runnable); } } private boolean createMapOrArrayMessageTask(Schema schema, boolean nested, Schema owner) { if (schema.getAdditionalProperties() != null) { String protoName = findWrapPropertyType(Map.class.getSimpleName(), (Schema) schema.getAdditionalProperties()); if (messages.add(protoName)) { pending.add(() -> wrapPropertyToMessage(protoName, schema)); createMessageTask((Schema) schema.getAdditionalProperties()); return true; } } if (schema.getItems() != null) { String protoName = findWrapPropertyType(List.class.getSimpleName(), schema.getItems()); if (messages.add(protoName)) { pending.add(() -> wrapPropertyToMessage(protoName, schema)); createMessageTask(schema.getItems()); return true; } } if (nested) { String protoName = owner.getAdditionalProperties() != null ? findWrapPropertyType(Map.class.getSimpleName(), schema) : findWrapPropertyType(List.class.getSimpleName(), schema); if (messages.add(protoName)) { pending.add(() -> wrapPropertyToMessage(protoName, owner)); return true; } } return false; } } ================================================ FILE: common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.utils; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.codec.protobuf.internal.converter.SwaggerToProtoGenerator; import org.apache.servicecomb.codec.protobuf.schema.SchemaToProtoGenerator; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.swagger.SwaggerUtils; import io.protostuff.compiler.model.Proto; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; /** * Manage swagger -> protoBuffer mappings. * * This class have the same lifecycle as MicroserviceMeta, so we need to create an instance * for each MicroserviceMeta. */ public class ScopedProtobufSchemaManager { static class SchemaKey { String schemaId; String rootMessage; Schema schema; int hashCode = -1; SchemaKey(String schemaId, String rootMessage, Schema schema) { this.schemaId = schemaId; this.rootMessage = rootMessage; this.schema = schema; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SchemaKey other = (SchemaKey) o; return StringUtils.equals(schemaId, other.schemaId) && StringUtils.equals(rootMessage, other.rootMessage) && SwaggerUtils.schemaEquals(schema, other.schema); } @Override public int hashCode() { if (hashCode != -1) { return hashCode; } hashCode = schemaId.hashCode() ^ rootMessage.hashCode() ^ SwaggerUtils.schemaHashCode(schema); return hashCode; } } // Because this class belongs to each SchemaMeta, the key is the schema id. private final Map mapperCache = new ConcurrentHashMapEx<>(); private final Map schemaMapperCache = new ConcurrentHashMapEx<>(); public ScopedProtobufSchemaManager() { } /** * get the ProtoMapper from Swagger */ public ProtoMapper getOrCreateProtoMapper(SchemaMeta schemaMeta) { return mapperCache.computeIfAbsent(schemaMeta.getSchemaId(), key -> { OpenAPI swagger = schemaMeta.getSwagger(); SwaggerToProtoGenerator generator = new SwaggerToProtoGenerator(schemaMeta.getMicroserviceQualifiedName(), swagger); Proto proto = generator.convert(); ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); return protoMapperFactory.create(proto); }); } /** * get the ProtoMapper from Schema */ public ProtoMapper getOrCreateProtoMapper(OpenAPI openAPI, String schemaId, String rootMessageName, Schema schema) { SchemaKey schemaKey = new SchemaKey(schemaId, rootMessageName, schema); return schemaMapperCache.computeIfAbsent(schemaKey, key -> { SchemaToProtoGenerator generator = new SchemaToProtoGenerator("scb.schema", openAPI, key.schema, rootMessageName); Proto proto = generator.convert(); ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); return protoMapperFactory.create(proto); }); } } ================================================ FILE: common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtostuff.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.protostuff.runtime.model; import java.util.ArrayList; import java.util.List; import java.util.Map; import io.protostuff.Tag; public class ModelProtostuff { @Tag(1) private String destMicroservice; @Tag(5) private String schemaId; @Tag(6) private String operationName; @Tag(7) private Map context; @Tag(8) private Map userMap; @Tag(9) private List list = new ArrayList<>(); @Tag(10) private List userList = new ArrayList<>(); //CHECKSTYLE:ON public String getDestMicroservice() { return destMicroservice; } public void setDestMicroservice(String destMicroservice) { this.destMicroservice = destMicroservice; } public String getSchemaId() { return schemaId; } public void setSchemaId(String schemaId) { this.schemaId = schemaId; } public String getOperationName() { return operationName; } public void setOperationName(String operationName) { this.operationName = operationName; } public Map getContext() { return context; } public void setContext(Map context) { this.context = context; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Map getUserMap() { return userMap; } public void setUserMap(Map userMap) { this.userMap = userMap; } public List getUserList() { return userList; } public void setUserList(List userList) { this.userList = userList; } } ================================================ FILE: common/common-protobuf/src/test/java/io/protostuff/runtime/model/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.protostuff.runtime.model; public class User { private String name; public User(String name) { this.name = name; } public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/definition/TestResponseRootDeserializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.definition; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.type.SimpleType; import com.fasterxml.jackson.databind.type.TypeFactory; import org.junit.jupiter.api.Test; class Model { } @SuppressWarnings({"rawtypes", "unchecked"}) public class TestResponseRootDeserializer { @Test public void testNeedConvert() { Assertions.assertEquals(SimpleType.constructUnsafe(Object.class), ProtoConst.OBJECT_TYPE); Assertions.assertFalse(ResponseRootDeserializer.needConvert(1, TypeFactory.defaultInstance().constructType(int.class))); Assertions.assertFalse(ResponseRootDeserializer.needConvert(1, TypeFactory.defaultInstance().constructType(Integer.class))); Assertions.assertFalse(ResponseRootDeserializer .needConvert(1, TypeFactory.defaultInstance().constructType(int.class))); Assertions.assertFalse(ResponseRootDeserializer .needConvert(1, TypeFactory.defaultInstance().constructType(Integer.class))); Assertions.assertTrue(ResponseRootDeserializer .needConvert(new HashMap<>(), TypeFactory.defaultInstance().constructType(Model.class))); Assertions.assertFalse(ResponseRootDeserializer .needConvert(new Model(), TypeFactory.defaultInstance().constructType(Model.class))); Assertions.assertFalse(ResponseRootDeserializer .needConvert(new Model(), TypeFactory.defaultInstance().constructType(Object.class))); List modelList = new ArrayList<>(); List modelMapList = new ArrayList<>(); Assertions.assertTrue(ResponseRootDeserializer .needConvert(modelMapList, TypeFactory.defaultInstance().constructType(new TypeReference>() { }))); // This case should be false, however it is not exists in real applications, for simpler, take it true. Assertions.assertTrue(ResponseRootDeserializer .needConvert(modelList, TypeFactory.defaultInstance().constructType(new TypeReference>() { }))); } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestProtoToStringGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; import org.junit.jupiter.api.Assertions; import io.protostuff.compiler.model.Proto; import org.junit.jupiter.api.Test; public class TestProtoToStringGenerator { static final String content = "syntax = \"proto3\";\n" + "import \"google/protobuf/any.proto\";\n" + "package org.apache.servicecomb.foundation.protobuf.internal.model;\n" + "\n" + "message Root {\n" + " int32 int32 = 1;\n" + " int64 int64 = 2;\n" + " uint32 uint32 = 3;\n" + " uint64 uint64 = 4;\n" + " sint32 sint32 = 5;\n" + " sint64 sint64 = 6;\n" + " fixed32 fixed32 = 7;\n" + " fixed64 fixed64 = 8;\n" + " sfixed32 sfixed32 = 9;\n" + " sfixed64 sfixed64 = 10;\n" + " float floatValue = 11;\n" + " double doubleValue = 12;\n" + " bool bool = 13;\n" + " string string = 14;\n" + " bytes bytes = 15;\n" + " Color color = 16;\n" + " User user = 17;\n" + " map ssMap = 18;\n" + " map spMap = 19;\n" + " repeated string sList = 20;\n" + " repeated User pList = 21;\n" + " google.protobuf.Any any = 22;\n" + " repeated google.protobuf.Any anys = 23;\n" + " Root typeRecursive = 24;\n" + "}\n" + "\n" + "message User {\n" + " string name = 1;\n" + " Root typeRecursive = 2;\n" + "}\n" + "\n" + "enum Color {\n" + " RED = 0;\n" + " YELLOW = 1;\n" + " BLUE = 2;\n" + "}\n" + "\n" + "service Service {\n" + " //comment\n" + " rpc op1 (User) returns (Root);\n" + "\n" + " rpc op2 (Root) returns (User);\n" + "}\n"; @Test public void protoToString() { ProtoParser protoParser = new ProtoParser(); Proto proto = protoParser.parseFromContent(content); String newContent = new ProtoToStringGenerator(proto).protoToString(); Assertions.assertEquals(content, newContent); } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import java.io.IOException; import java.time.LocalDate; import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootDeserializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchema; import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchemaPojo; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.People; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; import org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator; import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import com.fasterxml.jackson.databind.type.TypeFactory; /** * SchemaMetaCodec test cases. This test cases covers POJO invoker and producer. */ @SuppressWarnings({"rawtypes", "unchecked"}) public class TestSchemaMetaCodec { private SchemaMeta providerSchemaMeta; private SchemaMeta consumerSchemaMeta; @BeforeEach public void setUp() { ProtobufManager.clear(); } private void mockSchemaMeta(String schemaId, AbstractSwaggerGenerator swaggerGenerator, Object producerInstance) throws Exception { MicroserviceVersionsMeta microserviceVersionsMeta = Mockito.mock(MicroserviceVersionsMeta.class); ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getExecutorManager()).thenReturn(executorManager); MicroserviceMeta providerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); Mockito.when(providerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); Mockito.when(providerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); Mockito.when(providerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); Mockito.when(providerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); MicroserviceMeta consumerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); Mockito.when(consumerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); Mockito.when(consumerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); Mockito.when(consumerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); Mockito.when(consumerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); SwaggerProducer swaggerProducer = swaggerEnvironment.createProducer(producerInstance); providerSchemaMeta = new SchemaMeta(providerMicroserviceMeta, schemaId, swaggerProducer.getSwagger()); for (SwaggerProducerOperation producerOperation : swaggerProducer.getAllOperations()) { OperationMeta operationMeta = providerSchemaMeta.ensureFindOperation(producerOperation.getOperationId()); operationMeta.setSwaggerProducerOperation(producerOperation); } consumerSchemaMeta = new SchemaMeta(consumerMicroserviceMeta, schemaId, swaggerProducer.getSwagger()); } @Test public void testProtoSchemaOperationUserSpringMVC() throws Exception { mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); testProtoSchemaOperationUserImpl(); } @Test public void testProtoSchemaOperationUserPOJO() throws Exception { mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); testProtoSchemaOperationUserImpl(); } private Invocation mockInvocation(String operation, InvocationType invocationType) { OperationMeta operationMeta; boolean isProvider; Invocation invocation = Mockito.mock(Invocation.class); InvocationRuntimeType invocationRuntimeType; if (InvocationType.CONSUMER == invocationType) { operationMeta = consumerSchemaMeta.getOperations().get(operation); isProvider = false; Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); } else { operationMeta = providerSchemaMeta.getOperations().get(operation); isProvider = true; Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); } MicroserviceMeta microserviceMeta = operationMeta.getMicroserviceMeta(); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getInvocationRuntimeType()) .thenReturn(invocationRuntimeType); Mockito.when(invocation.findResponseType(200)) .thenReturn(invocationRuntimeType.findResponseType(200)); Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(invocation.isProducer()).thenReturn(isProvider); return invocation; } private void testProtoSchemaOperationUserImpl() throws IOException { Invocation consumerInvocation = mockInvocation("user", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("user", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); User user = new User(); user.name = "user"; User friend = new User(); friend.name = "friend"; List friends = new ArrayList<>(); friends.add(friend); user.friends = friends; byte[] values; // request message Map args = new HashMap<>(); RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); user.friends = friends; args.put("user", user); values = requestSerializer.serialize(args); RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedUserArgs = requestDeserializer.deserialize(values); Assertions.assertEquals(user.name, ((User) decodedUserArgs.get("user")).name); Assertions.assertEquals(user.friends.get(0).name, ((User) decodedUserArgs.get("user")).friends.get(0).name); // write request map (pojo) args = new HashMap<>(); Map userMap = new HashMap<>(); userMap.put("name", "user"); Map friendMap = new HashMap<>(); friendMap.put("name", "friend"); List> friendsList = new ArrayList<>(); friendsList.add(friendMap); userMap.put("friends", friendsList); args.put("user", userMap); values = requestSerializer.serialize(args); decodedUserArgs = requestDeserializer.deserialize(values); Assertions.assertEquals(user.name, ((User) decodedUserArgs.get("user")).name); Assertions.assertEquals(user.friends.get(0).name, ((User) decodedUserArgs.get("user")).friends.get(0).name); // response message ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); values = responseSerializer.serialize(user); ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); User decodedUser = (User) responseDeserializer .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); Assertions.assertEquals(user.name, decodedUser.name); Assertions.assertEquals(user.friends.get(0).name, decodedUser.friends.get(0).name); user.friends = new ArrayList<>(); values = responseSerializer.serialize(user); decodedUser = (User) responseDeserializer .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); Assertions.assertEquals(user.name, decodedUser.name); // proto buffer encode and decode empty list to be null Assertions.assertNull(decodedUser.friends); } @Test public void testProtoSchemaOperationmapUserSpringMVC() throws Exception { mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); testProtoSchemaOperationmapUserImpl(false); } @Test public void testProtoSchemaOperationmapUserPOJO() throws Exception { mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); testProtoSchemaOperationmapUserImpl(true); } private void testProtoSchemaOperationmapUserImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("mapUser", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("mapUser", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); User user = new User(); user.name = "user"; User friend = new User(); friend.name = "friend"; List friends = new ArrayList<>(); friends.add(friend); user.friends = friends; byte[] values; Map userMap = new HashMap<>(); userMap.put("test", user); // request message Map args = new HashMap<>(); RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); user.friends = friends; args.put("users", userMap); if (isPojo) { Map swaggerArgs = new HashMap<>(1); swaggerArgs.put("listListUserBody", args); values = requestSerializer.serialize(swaggerArgs); } else { values = requestSerializer.serialize(args); } RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedUserArgs = requestDeserializer.deserialize(values); if (isPojo) { decodedUserArgs = (Map) decodedUserArgs.get("mapUserBody"); Assertions.assertEquals(user.name, ((Map>) decodedUserArgs.get("users")).get("test").get("name")); Assertions.assertEquals(user.friends.get(0).name, ((List>) ((Map>) decodedUserArgs.get("users")).get("test") .get("friends")).get(0).get("name")); } else { Assertions.assertEquals(user.name, ((Map) decodedUserArgs.get("users")).get("test").name); Assertions.assertEquals(user.friends.get(0).name, ((Map) decodedUserArgs.get("users")).get("test").friends.get(0).name); } // response message ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); values = responseSerializer.serialize(userMap); ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); Map decodedUser = (Map) responseDeserializer.deserialize(values, TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, User.class)); Assertions.assertEquals(user.name, decodedUser.get("test").name); Assertions.assertEquals(user.friends.get(0).name, decodedUser.get("test").friends.get(0).name); user.friends = new ArrayList<>(); values = responseSerializer.serialize(userMap); decodedUser = (Map) responseDeserializer.deserialize(values, TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, User.class)); Assertions.assertEquals(user.name, decodedUser.get("test").name); // proto buffer encode and decode empty list to be null Assertions.assertNull(decodedUser.get("test").friends); } @Test public void testProtoSchemaOperationBaseSpringMVC() throws Exception { mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); testProtoSchemaOperationBaseImpl(false); } @Test public void testProtoSchemaOperationBasePOJO() throws Exception { mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); testProtoSchemaOperationBaseImpl(true); } private void testProtoSchemaOperationBaseImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("base", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("base", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); byte[] values; // request message RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); boolean boolValue = true; int iValue = 20; long lValue = 30L; float fValue = 40f; double dValue = 50D; String sValue = "hello"; int[] iArray = new int[] {60, 70}; Color color = Color.BLUE; LocalDate localDate = LocalDate.of(2019, 10, 1); Date date = new Date(); Empty empty = new Empty(); Map args = new HashMap<>(); args.put("boolValue", boolValue); args.put("iValue", iValue); args.put("lValue", lValue); args.put("fValue", fValue); args.put("dValue", dValue); args.put("sValue", sValue); args.put("iArray", iArray); args.put("color", color); args.put("localDate", localDate); args.put("date", date); args.put("empty", empty); if (isPojo) { Map swaggerArgs = new HashMap<>(); swaggerArgs.put("baseBody", args); values = requestSerializer.serialize(swaggerArgs); } else { values = requestSerializer.serialize(args); } RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedSwaggerArgs = requestDeserializer.deserialize(values); Map decodedArgs; if (isPojo) { Assertions.assertEquals(1, decodedSwaggerArgs.size()); decodedArgs = (Map) decodedSwaggerArgs.get("baseBody"); } else { decodedArgs = decodedSwaggerArgs; } Assertions.assertEquals(boolValue, decodedArgs.get("boolValue")); Assertions.assertEquals(iValue, decodedArgs.get("iValue")); Assertions.assertEquals(lValue, decodedArgs.get("lValue")); Assertions.assertEquals(fValue, decodedArgs.get("fValue")); Assertions.assertEquals(dValue, decodedArgs.get("dValue")); if (isPojo) { Assertions.assertEquals(2, ((List) decodedArgs.get("iArray")).size()); Assertions.assertEquals(60, (((List) decodedArgs.get("iArray")).get(0).intValue())); Assertions.assertEquals(70, (((List) decodedArgs.get("iArray")).get(1).intValue())); Assertions.assertEquals(color.ordinal(), decodedArgs.get("color")); Assertions.assertEquals(date.getTime(), decodedArgs.get("date")); Assertions.assertEquals(localDate.getLong(ChronoField.EPOCH_DAY), decodedArgs.get("localDate")); Assertions.assertTrue(((Map) decodedArgs.get("empty")).isEmpty()); } else { Assertions.assertArrayEquals(iArray, (int[]) decodedArgs.get("iArray")); Assertions.assertEquals(color, decodedArgs.get("color")); Assertions.assertEquals(date, decodedArgs.get("date")); Assertions.assertTrue(decodedArgs.get("localDate") instanceof LocalDate); Assertions.assertEquals(localDate, decodedArgs.get("localDate")); Assertions.assertTrue(decodedArgs.get("empty") instanceof Empty); } // default value testing args.put("boolValue", false); args.put("iValue", 0); args.put("lValue", 0L); args.put("fValue", 0F); args.put("dValue", 0D); args.put("sValue", null); args.put("iArray", new int[0]); args.put("color", null); args.put("localDate", null); args.put("date", null); args.put("empty", null); values = requestSerializer.serialize(args); decodedArgs = requestDeserializer.deserialize(values); Assertions.assertNull(decodedArgs.get("boolValue")); Assertions.assertNull(decodedArgs.get("iValue")); Assertions.assertNull(decodedArgs.get("lValue")); Assertions.assertNull(decodedArgs.get("fValue")); Assertions.assertNull(decodedArgs.get("dValue")); Assertions.assertNull(decodedArgs.get("iArray")); Assertions.assertNull(decodedArgs.get("color")); Assertions.assertNull(decodedArgs.get("localDate")); Assertions.assertNull(decodedArgs.get("date")); Assertions.assertNull(decodedArgs.get("empty")); // response message ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); values = responseSerializer.serialize(30); ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); Object decodedValue = responseDeserializer .deserialize(values, TypeFactory.defaultInstance().constructType(int.class)); Assertions.assertEquals(30, (int) decodedValue); } @Test public void testProtoSchemaOperationlistListUserSpringMVC() throws Exception { mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); testProtoSchemaOperationlistListUserImpl(false); } @Test public void testProtoSchemaOperationlistListUserPOJO() throws Exception { mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); testProtoSchemaOperationlistListUserImpl(true); } private void testProtoSchemaOperationlistListUserImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("listListUser", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("listListUser", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); byte[] values; // request message RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); User user = new User(); user.name = "user"; User friend = new User(); friend.name = "friend"; List friends = new ArrayList<>(); friends.add(friend); user.friends = friends; List users = new ArrayList<>(); users.add(user); List> listOfUsers = new ArrayList<>(); listOfUsers.add(users); Map args = new HashMap<>(); args.put("value", listOfUsers); if (isPojo) { Map swaggerArgs = new HashMap<>(); swaggerArgs.put("listListUserBody", args); values = requestSerializer.serialize(swaggerArgs); } else { values = requestSerializer.serialize(args); } RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedSwaggerArgs = requestDeserializer.deserialize(values); Map decodedArgs; if (isPojo) { Assertions.assertEquals(1, decodedSwaggerArgs.size()); decodedArgs = (Map) decodedSwaggerArgs.get("listListUserBody"); } else { decodedArgs = decodedSwaggerArgs; } List> listOfUsersRaw = (List>) decodedArgs.get("value"); Assertions.assertEquals(1, listOfUsersRaw.size()); List mapUsersRaw = (List) listOfUsersRaw.get(0); Assertions.assertEquals(1, mapUsersRaw.size()); if (isPojo) { Map userMap = (Map) mapUsersRaw.get(0); Assertions.assertEquals("user", userMap.get("name")); // proto buffer encode and decode empty list to be null friends = (List) userMap.get("friends"); Map friendMap = (Map) friends.get(0); Assertions.assertEquals("friend", friendMap.get("name")); } else { user = (User) mapUsersRaw.get(0); Assertions.assertEquals("user", user.name); // proto buffer encode and decode empty list to be null Assertions.assertEquals("friend", user.friends.get(0).name); } } @Test public void testProtoSchemaOperationObjSpringMVC() throws Exception { mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); testProtoSchemaOperationObjImpl(false); } @Test public void testProtoSchemaOperationObjPOJO() throws Exception { mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); testProtoSchemaOperationObjImpl(true); } private void testProtoSchemaOperationObjImpl(boolean isPojo) throws IOException { Invocation consumerInvocation = mockInvocation("obj", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("obj", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); byte[] values; // request message RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); Map args = new HashMap<>(); args.put("value", 2); values = requestSerializer.serialize(args); RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedArgs = requestDeserializer.deserialize(values); int result = (int) decodedArgs.get("value"); Assertions.assertEquals(2, result); User user = new User(); user.name = "user"; User friend = new User(); friend.name = "friend"; List friends = new ArrayList<>(); friends.add(friend); user.friends = friends; args.put("value", user); values = requestSerializer.serialize(args); decodedArgs = requestDeserializer.deserialize(values); Map userMap = (Map) decodedArgs.get("value"); Assertions.assertEquals("user", userMap.get("name")); // proto buffer encode and decode empty list to be null friends = (List) userMap.get("friends"); Map friendMap = (Map) friends.get(0); Assertions.assertEquals("friend", friendMap.get("name")); args.clear(); People people = new People(); people.name = "user"; People pFriend = new People(); pFriend.name = "friend"; List pFriends = new ArrayList<>(); pFriends.add(pFriend); people.friends = pFriends; args.put("value", people); values = requestSerializer.serialize(args); decodedArgs = requestDeserializer.deserialize(values); people = (People) decodedArgs.get("value"); Assertions.assertEquals("user", people.name); // proto buffer encode and decode empty list to be null Assertions.assertEquals("friend", people.friends.get(0).name); } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootDeserializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchema; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import com.fasterxml.jackson.databind.type.TypeFactory; /** * SchemaMetaCodec test cases. This test cases covers RestTemplate invoker and producer. */ public class TestSchemaMetaCodecRestTemplate { private SchemaMeta providerSchemaMeta; private SchemaMeta consumerSchemaMeta; @BeforeEach public void setUp() { ProtobufManager.clear(); MicroserviceVersionsMeta microserviceVersionsMeta = Mockito.mock(MicroserviceVersionsMeta.class); ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getExecutorManager()).thenReturn(executorManager); MicroserviceMeta providerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); Mockito.when(providerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); Mockito.when(providerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); Mockito.when(providerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); Mockito.when(providerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); MicroserviceMeta consumerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); Mockito.when(consumerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); Mockito.when(consumerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); Mockito.when(consumerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); Mockito.when(consumerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); SpringmvcSwaggerGenerator swaggerGenerator = new SpringmvcSwaggerGenerator(ProtoSchema.class); SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); SwaggerProducer swaggerProducer = swaggerEnvironment.createProducer(new ProtoSchema()); providerSchemaMeta = new SchemaMeta(providerMicroserviceMeta, "ProtoSchema", swaggerProducer.getSwagger()); for (SwaggerProducerOperation producerOperation : swaggerProducer.getAllOperations()) { OperationMeta operationMeta = providerSchemaMeta.ensureFindOperation(producerOperation.getOperationId()); operationMeta.setSwaggerProducerOperation(producerOperation); } consumerSchemaMeta = new SchemaMeta(consumerMicroserviceMeta, "ProtoSchema", swaggerProducer.getSwagger()); } private Invocation mockInvocation(String operation, InvocationType invocationType) { OperationMeta operationMeta; boolean isProvider; Invocation invocation = Mockito.mock(Invocation.class); InvocationRuntimeType invocationRuntimeType; if (InvocationType.CONSUMER == invocationType) { operationMeta = consumerSchemaMeta.getOperations().get(operation); isProvider = false; Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); } else { operationMeta = providerSchemaMeta.getOperations().get(operation); isProvider = true; Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); } MicroserviceMeta microserviceMeta = operationMeta.getMicroserviceMeta(); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getInvocationRuntimeType()) .thenReturn(invocationRuntimeType); Mockito.when(invocation.findResponseType(200)) .thenReturn(invocationRuntimeType.findResponseType(200)); Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(invocation.isProducer()).thenReturn(isProvider); return invocation; } @Test public void testProtoSchemaOperationUser() throws Exception { Invocation consumerInvocation = mockInvocation("user", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("user", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); User user = new User(); user.name = "user"; User friend = new User(); friend.name = "friend"; List friends = new ArrayList<>(); friends.add(friend); user.friends = friends; byte[] values; // request message Map args = new HashMap<>(); RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); user.friends = friends; args.put("user", user); values = requestSerializer.serialize(args); RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedUserArgs = requestDeserializer.deserialize(values); Assertions.assertEquals(user.name, ((User) decodedUserArgs.get("user")).name); Assertions.assertEquals(user.friends.get(0).name, ((User) decodedUserArgs.get("user")).friends.get(0).name); // response message ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); values = responseSerializer.serialize(user); ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); User decodedUser = (User) responseDeserializer .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); Assertions.assertEquals(user.name, decodedUser.name); Assertions.assertEquals(user.friends.get(0).name, decodedUser.friends.get(0).name); user.friends = new ArrayList<>(); values = responseSerializer.serialize(user); decodedUser = (User) responseDeserializer .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); Assertions.assertEquals(user.name, decodedUser.name); // proto buffer encode and decode empty list to be null Assertions.assertNull(decodedUser.friends); } @Test @SuppressWarnings({"rawtypes", "unchecked"}) public void testProtoSchemaOperationBase() throws Exception { Invocation consumerInvocation = mockInvocation("base", InvocationType.CONSUMER); Invocation providerInvocation = mockInvocation("base", InvocationType.PROVIDER); OperationProtobuf providerOperationProtobuf = ProtobufManager .getOrCreateOperation(providerInvocation); OperationProtobuf consumerOperationProtobuf = ProtobufManager .getOrCreateOperation(consumerInvocation); byte[] values; // request message RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); boolean boolValue = true; int iValue = 20; long lValue = 30L; float fValue = 40f; double dValue = 50D; String sValue = "hello"; int[] iArray = new int[] {60, 70}; Color color = Color.BLUE; LocalDate localDate = LocalDate.of(2019, 10, 1); Date date = new Date(); Empty empty = new Empty(); Map args = new HashMap<>(); args.put("boolValue", boolValue); args.put("iValue", iValue); args.put("lValue", lValue); args.put("fValue", fValue); args.put("dValue", dValue); args.put("sValue", sValue); args.put("iArray", iArray); args.put("color", color); args.put("localDate", localDate); args.put("date", date); args.put("empty", empty); values = requestSerializer.serialize(args); RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); Map decodedArgs = requestDeserializer.deserialize(values); Assertions.assertEquals(boolValue, decodedArgs.get("boolValue")); Assertions.assertEquals(iValue, decodedArgs.get("iValue")); Assertions.assertEquals(lValue, decodedArgs.get("lValue")); Assertions.assertEquals(fValue, decodedArgs.get("fValue")); Assertions.assertEquals(dValue, decodedArgs.get("dValue")); Assertions.assertArrayEquals(iArray, (int[]) decodedArgs.get("iArray")); Assertions.assertEquals(color, decodedArgs.get("color")); Assertions.assertEquals(date, decodedArgs.get("date")); Assertions.assertTrue(decodedArgs.get("localDate") instanceof LocalDate); Assertions.assertEquals(localDate, decodedArgs.get("localDate")); Assertions.assertTrue(decodedArgs.get("empty") instanceof Empty); // default value testing args.put("boolValue", false); args.put("iValue", 0); args.put("lValue", 0L); args.put("fValue", 0F); args.put("dValue", 0D); args.put("sValue", null); args.put("iArray", new int[0]); args.put("color", null); args.put("localDate", null); args.put("date", null); args.put("empty", null); values = requestSerializer.serialize(args); decodedArgs = requestDeserializer.deserialize(values); Assertions.assertNull(decodedArgs.get("boolValue")); Assertions.assertNull(decodedArgs.get("iValue")); Assertions.assertNull(decodedArgs.get("lValue")); Assertions.assertNull(decodedArgs.get("fValue")); Assertions.assertNull(decodedArgs.get("dValue")); Assertions.assertNull(decodedArgs.get("iArray")); Assertions.assertNull(decodedArgs.get("color")); Assertions.assertNull(decodedArgs.get("localDate")); Assertions.assertNull(decodedArgs.get("date")); Assertions.assertNull(decodedArgs.get("empty")); // response message ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); values = responseSerializer.serialize(30); ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); Object decodedValue = responseDeserializer .deserialize(values, TypeFactory.defaultInstance().constructType(int.class)); Assertions.assertEquals(30, (int) decodedValue); } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSwaggerToProtoGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchema; import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.protostuff.compiler.model.Proto; import io.swagger.v3.oas.models.OpenAPI; public class TestSwaggerToProtoGenerator { @Test public void convert() throws IOException { URL url = TestSwaggerToProtoGenerator.class.getClassLoader().getResource("ProtoSchema.proto"); String protoContent = IOUtils.toString(url, StandardCharsets.UTF_8); int idx = protoContent.indexOf("syntax = "); protoContent = protoContent.substring(idx); SpringmvcSwaggerGenerator swaggerGenerator = new SpringmvcSwaggerGenerator(ProtoSchema.class); OpenAPI swagger = swaggerGenerator.generate(); SwaggerToProtoGenerator generator = new SwaggerToProtoGenerator("a.b", swagger); Proto proto = generator.convert(); Assertions.assertEquals(protoContent.replaceAll("\r\n", "\n"), new ProtoToStringGenerator(proto).protoToString().replaceAll("\r\n", "\n")); } @Test public void testEscape() { Assertions.assertEquals("hello_my_service", SwaggerToProtoGenerator.escapeMessageName("hello.my.service")); Assertions.assertEquals("hello_my_service", SwaggerToProtoGenerator.escapeMessageName("hello_my_service")); Assertions.assertEquals("hello.my_service", SwaggerToProtoGenerator.escapePackageName("hello.my-service")); Assertions.assertEquals("hello.test.test", SwaggerToProtoGenerator.escapePackageName("hello.test.test")); Assertions.assertEquals("hello_my.test.test", SwaggerToProtoGenerator.escapePackageName("hello:my.test.test")); Assertions.assertFalse(SwaggerToProtoGenerator.isValidEnum("hello.test.test")); Assertions.assertFalse(SwaggerToProtoGenerator.isValidEnum("hello.my-service")); Assertions.assertTrue(SwaggerToProtoGenerator.isValidEnum("My_ENum")); } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/FieldNeedWrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter.model; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.User; public class FieldNeedWrap { public List> listListUser; public List> listMapUser; public Map> mapListUser; public Map> mapMapUser; public List>> listListListUser; public List>> listListMapUser; public List>> listMapListUser; public List>> listMapMapUser; public Map>> mapMapListUser; public Map>> mapMapMapUser; } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter.model; import java.time.LocalDate; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; @RequestMapping(path = "/") public class ProtoSchema implements ProtoSchemaIntf { @ApiResponses(value = { @ApiResponse(responseCode = "444", content = @Content(schema = @Schema(implementation = Color.class)), description = "xxx")}) @GetMapping(path = "/base") public int base(boolean boolValue, int iValue, long lValue, float fValue, double dValue, String sValue, int[] iArray, Color color, LocalDate localDate, Date date, @RequestBody Empty empty) { return 0; } @GetMapping(path = "/bytes") public byte[] bytes(@RequestBody byte[] value) { return null; } @GetMapping(path = "/colorBody") public Color colorBody(@RequestBody Color color) { return null; } @GetMapping(path = "/obj") public Object obj(@RequestBody Object value) { return null; } @GetMapping(path = "/user") public User user(@RequestBody User user) { return null; } @GetMapping(path = "/userWrapInProtobuf") public User userWrapInProtobuf(@RequestBody User user, int ivalue) { return null; } @GetMapping(path = "/listObj") public List listObj(@RequestBody List objs) { return null; } @GetMapping(path = "/listUser") public List listUser(@RequestBody List users) { return null; } @GetMapping(path = "/mapUser") public Map mapUser(@RequestBody Map users) { return null; } @GetMapping(path = "/mapObj") public Map mapObj(@RequestBody Map objs) { return null; } @GetMapping(path = "/ref") public Ref2 ref(@RequestBody Ref1 ref) { return null; } @GetMapping(path = "/noParamVoid") public void noParamVoid() { } @PostMapping(path = "/listListString") public List> listListString(@RequestBody List> value) { return value; } @PostMapping(path = "/listListUser") public List> listListUser(@RequestBody List> value) { return value; } @PostMapping(path = "/listMapString") public List> listMapString(@RequestBody List> value) { return value; } @PostMapping(path = "/listMapUser") public List> listMapUser(@RequestBody List> value) { return value; } @PostMapping(path = "/mapListString") public Map> mapListString(@RequestBody Map> value) { return value; } @PostMapping(path = "/mapListUser") public Map> mapListUser(@RequestBody Map> value) { return value; } @PostMapping(path = "/mapMapString") public Map> mapMapString(@RequestBody Map> value) { return value; } @PostMapping(path = "/mapMapUser") public Map> mapMapUser(@RequestBody Map> value) { return value; } @PostMapping(path = "/listListListString") public List>> listListListString(@RequestBody List>> value) { return value; } @PostMapping(path = "/listListMapString") public List>> listListMapString(@RequestBody List>> value) { return value; } @PostMapping(path = "/listMapListString") public List>> listMapListString(@RequestBody List>> value) { return value; } @PostMapping(path = "/listMapMapString") public List>> listMapMapString( @RequestBody List>> value) { return value; } @PostMapping(path = "/mapMapListString") public Map>> mapMapListString( @RequestBody Map>> value) { return value; } @PostMapping(path = "/mapMapMapString") public Map>> mapMapMapString( @RequestBody Map>> value) { return value; } @PostMapping(path = "/fieldNeedWrap") public FieldNeedWrap fieldNeedWrap(@RequestBody FieldNeedWrap fieldNeedWrap) { return fieldNeedWrap; } @PostMapping(path = "/testTextPlain", consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_PLAIN) public String testTextPlain(@RequestBody String fieldNeedWrap) { return null; } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter.model; import java.time.LocalDate; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.User; public interface ProtoSchemaIntf { int base(boolean boolValue, int iValue, long lValue, float fValue, double dValue, String sValue, int[] iArray, Color color, LocalDate localDate, Date date, Empty empty); byte[] bytes(byte[] value); Color colorBody(Color color); Object obj(Object value); User user(User user); User userWrapInProtobuf(User user, int ivalue); List listObj(List objs); List listUser(List users); Map mapUser(Map users); Map mapObj(Map objs); Ref2 ref(Ref1 ref); void noParamVoid(); List> listListString(List> value); List> listListUser(List> value); List> listMapString(List> value); List> listMapUser(List> value); Map> mapListString(Map> value); Map> mapListUser(Map> value); Map> mapMapString(Map> value); Map> mapMapUser(Map> value); List>> listListListString(List>> value); List>> listListMapString(List>> value); List>> listMapListString(List>> value); List>> listMapMapString( List>> value); Map>> mapMapListString( Map>> value); Map>> mapMapMapString( Map>> value); FieldNeedWrap fieldNeedWrap(FieldNeedWrap fieldNeedWrap); } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaPojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter.model; import java.time.LocalDate; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; public class ProtoSchemaPojo implements ProtoSchemaIntf { @ApiResponses(value = { @ApiResponse(responseCode = "444", content = @Content(schema = @Schema(implementation = Color.class)), description = "xxx")}) public int base(boolean boolValue, int iValue, long lValue, float fValue, double dValue, String sValue, int[] iArray, Color color, LocalDate localDate, Date date, Empty empty) { return 0; } public byte[] bytes(byte[] value) { return null; } public Color colorBody(Color color) { return null; } public Object obj(Object value) { return null; } public User user(User user) { return null; } public User userWrapInProtobuf(User user, int ivalue) { return null; } public List listObj(List objs) { return null; } public List listUser(List users) { return null; } public Map mapUser(Map users) { return null; } public Map mapObj(Map objs) { return null; } public Ref2 ref(Ref1 ref) { return null; } public void noParamVoid() { } public List> listListString(List> value) { return value; } public List> listListUser(List> value) { return value; } public List> listMapString(List> value) { return value; } public List> listMapUser(List> value) { return value; } public Map> mapListString(Map> value) { return value; } public Map> mapListUser(Map> value) { return value; } public Map> mapMapString(Map> value) { return value; } public Map> mapMapUser(Map> value) { return value; } public List>> listListListString(List>> value) { return value; } public List>> listListMapString(List>> value) { return value; } public List>> listMapListString(List>> value) { return value; } public List>> listMapMapString( List>> value) { return value; } public Map>> mapMapListString( Map>> value) { return value; } public Map>> mapMapMapString( Map>> value) { return value; } public FieldNeedWrap fieldNeedWrap(FieldNeedWrap fieldNeedWrap) { return fieldNeedWrap; } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter.model; public class Ref1 { public Ref2 ref; } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.internal.converter.model; public class Ref2 { public Ref1 ref; } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema; import static org.junit.jupiter.api.Assertions.assertEquals; import java.math.BigDecimal; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.codec.protobuf.schema.model.DeptInfo; import org.apache.servicecomb.codec.protobuf.schema.model.SchemaService; import org.apache.servicecomb.codec.protobuf.schema.model.ScoreInfo; import org.apache.servicecomb.codec.protobuf.schema.model.UserInfo; import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.NumberSchema; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.StringSchema; import jakarta.ws.rs.core.MediaType; public class TestSchemaCodec { ScopedProtobufSchemaManager manager = new ScopedProtobufSchemaManager(); @Test public void test_string_schema_codec() throws Exception { OpenAPI openAPI = new OpenAPI(); StringSchema schema = new StringSchema(); ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "test", "input", schema); RootSerializer serializer = protoMapper.getSerializerSchemaManager() .createRootSerializer(protoMapper.getProto().getMessage("input"), String.class); Map arguments = new HashMap<>(); arguments.put("value", "abcdefg"); byte[] result = serializer.serialize(arguments); RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() .createRootDeserializer(protoMapper.getProto().getMessage("input"), String.class); PropertyWrapper deserializedResult = deserializer.deserialize(result); assertEquals("abcdefg", deserializedResult.getValue()); } @Test public void test_number_schema_codec() throws Exception { OpenAPI openAPI = new OpenAPI(); NumberSchema schema = new NumberSchema(); ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "test", "input", schema); RootSerializer serializer = protoMapper.getSerializerSchemaManager() .createRootSerializer(protoMapper.getProto().getMessage("input"), BigDecimal.class); Map arguments = new HashMap<>(); BigDecimal number = new BigDecimal(10); arguments.put("value", number); byte[] result = serializer.serialize(arguments); RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() .createRootDeserializer(protoMapper.getProto().getMessage("input"), BigDecimal.class); PropertyWrapper deserializedResult = deserializer.deserialize(result); assertEquals(number, deserializedResult.getValue()); } public static class User { public String name; } @Test public void test_object_schema_codec() throws Exception { OpenAPI openAPI = new OpenAPI(); ObjectSchema schema = new ObjectSchema(); schema.setName("User"); schema.addProperty("name", new StringSchema()); openAPI.setComponents(new Components()); openAPI.getComponents().addSchemas("User", schema); ObjectSchema ref = new ObjectSchema(); ref.set$ref(Components.COMPONENTS_SCHEMAS_REF + "User"); ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "test", "input", ref); RootSerializer serializer = protoMapper.getSerializerSchemaManager() .createRootSerializer(protoMapper.getProto().getMessage("input"), User.class); Map arguments = new HashMap<>(); User user = new User(); user.name = "abcdefg"; arguments.put("value", user); byte[] result = serializer.serialize(arguments); RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() .createRootDeserializer(protoMapper.getProto().getMessage("input"), User.class); PropertyWrapper deserializedResult = deserializer.deserialize(result); assertEquals("abcdefg", deserializedResult.getValue().name); } @Test public void test_springmvc_model_schema_codec_correct() throws Exception { SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); OpenAPI openAPI = generator.generate(); ProtoMapper protoMapper = manager.getOrCreateProtoMapper(openAPI, "schemaService", "input", openAPI.getPaths().get("/testUserInfo").getPost() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) .getSchema()); RootSerializer serializer = protoMapper.getSerializerSchemaManager() .createRootSerializer(protoMapper.getProto().getMessage("input"), UserInfo.class); Map arguments = new HashMap<>(); UserInfo userInfo = new UserInfo(); DeptInfo deptInfo = new DeptInfo(); deptInfo.setCode("123"); ScoreInfo scoreInfo = new ScoreInfo(); scoreInfo.setType(233); deptInfo.setScores(List.of(scoreInfo)); userInfo.setSubDeptInfos(List.of(deptInfo)); arguments.put("value", userInfo); byte[] result = serializer.serialize(arguments); RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() .createRootDeserializer(protoMapper.getProto().getMessage("input"), UserInfo.class); PropertyWrapper deserializedResult = deserializer.deserialize(result); assertEquals(1, deserializedResult.getValue().getSubDeptInfos().size()); assertEquals("123", deserializedResult.getValue().getSubDeptInfos().get(0).getCode()); assertEquals(233, deserializedResult.getValue().getSubDeptInfos().get(0).getScores().get(0).getType()); } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.servicecomb.codec.protobuf.internal.converter.ProtoToStringGenerator; import org.apache.servicecomb.codec.protobuf.schema.model.SchemaService; import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import io.protostuff.compiler.model.Proto; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.StringSchema; import jakarta.ws.rs.core.MediaType; //CHECKSTYLE:OFF @SuppressWarnings("unused") public class TestSchemaToProtoGenerator { @Test public void test_string_schema_is_correct() { OpenAPI openAPI = new OpenAPI(); StringSchema schema = new StringSchema(); SchemaToProtoGenerator generator = new SchemaToProtoGenerator("test.string", openAPI, schema, "input"); Proto proto = generator.convert(); assertEquals(""" syntax = "proto3"; package test.string; //@WrapProperty message input { string value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test public void test_object_schema_is_correct() { OpenAPI openAPI = new OpenAPI(); ObjectSchema schema = new ObjectSchema(); schema.setName("User"); schema.addProperty("name", new StringSchema()); openAPI.setComponents(new Components()); openAPI.getComponents().addSchemas("User", schema); ObjectSchema ref = new ObjectSchema(); ref.set$ref(Components.COMPONENTS_SCHEMAS_REF + "User"); SchemaToProtoGenerator generator = new SchemaToProtoGenerator("test.object", openAPI, ref, "input"); Proto proto = generator.convert(); assertEquals(""" syntax = "proto3"; package test.object; message User { string name = 1; } //@WrapProperty message input { User value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } static class Model { public String name; public int age; } interface SpringMvcSchema { @PostMapping("/testInt") int testInt(@RequestBody int param); @PostMapping("/testModel") Model testModel(@RequestBody Model model); } @Test public void test_springmvc_int_schema_correct() { SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SpringMvcSchema.class); OpenAPI openAPI = generator.generate(); SchemaToProtoGenerator protoGenerator = new SchemaToProtoGenerator("test.int", openAPI, openAPI.getPaths().get("/testInt").getPost() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "testIntRequest"); Proto proto = protoGenerator.convert(); assertEquals(""" syntax = "proto3"; package test.int; //@WrapProperty message testIntRequest { sint32 value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); protoGenerator = new SchemaToProtoGenerator("test.int", openAPI, openAPI.getPaths().get("/testInt").getPost() .getResponses().get("200").getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "testIntResponse"); proto = protoGenerator.convert(); assertEquals(""" syntax = "proto3"; package test.int; //@WrapProperty message testIntResponse { sint32 value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test public void test_springmvc_model_schema_correct() { SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SpringMvcSchema.class); OpenAPI openAPI = generator.generate(); SchemaToProtoGenerator protoGenerator = new SchemaToProtoGenerator("test.model", openAPI, openAPI.getPaths().get("/testModel").getPost() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "testModelRequest"); Proto proto = protoGenerator.convert(); assertEquals(""" syntax = "proto3"; package test.model; message Model { sint32 age = 1; string name = 2; } //@WrapProperty message testModelRequest { Model value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); protoGenerator = new SchemaToProtoGenerator("test.model", openAPI, openAPI.getPaths().get("/testModel").getPost() .getResponses().get("200").getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "testIntResponse"); proto = protoGenerator.convert(); assertEquals(""" syntax = "proto3"; package test.model; message Model { sint32 age = 1; string name = 2; } //@WrapProperty message testIntResponse { Model value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test public void testNestedModelCorrect() { SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); OpenAPI openAPI = generator.generate(); SchemaToProtoGenerator protoGenerator = new SchemaToProtoGenerator("test.model", openAPI, openAPI.getPaths().get("/testUserInfo").getPost() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "request"); Proto proto = protoGenerator.convert(); assertEquals(""" syntax = "proto3"; package test.model; //@WrapProperty message MapString { map value = 1; } //@WrapProperty message ListListString { repeated ListString value = 1; } //@WrapProperty message ListString { repeated string value = 1; } message ScoreInfo { sint32 type = 1; } message DeptInfo { string code = 1; string name = 2; repeated ScoreInfo scores = 3; } message UserInfo { repeated MapString extraInfos = 1; repeated ListListString nestedLists = 2; repeated DeptInfo subDeptInfos = 3; } //@WrapProperty message ListScoreInfo { repeated ScoreInfo value = 1; } //@WrapProperty message ListDeptInfo { repeated DeptInfo value = 1; } //@WrapProperty message request { UserInfo value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test public void testListMapTypeCorrect() { SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); OpenAPI openAPI = generator.generate(); SchemaToProtoGenerator protoGenerator = new SchemaToProtoGenerator("test.model", openAPI, openAPI.getPaths().get("/testListType").getPost() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "request"); Proto proto = protoGenerator.convert(); assertEquals(""" syntax = "proto3"; package test.model; message ScoreInfo { sint32 type = 1; } message DeptInfo { string code = 1; string name = 2; repeated ScoreInfo scores = 3; } //@WrapProperty message ListScoreInfo { repeated ScoreInfo value = 1; } //@WrapProperty message ListDeptInfo { repeated DeptInfo value = 1; } //@WrapProperty message request { repeated DeptInfo value = 1; } """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test public void testCyclicModelWrong() { SpringmvcSwaggerGenerator generator = new SpringmvcSwaggerGenerator(SchemaService.class); OpenAPI openAPI = generator.generate(); SchemaToProtoGenerator protoGenerator = new SchemaToProtoGenerator("test.model", openAPI, openAPI.getPaths().get("/testCyclic").getPost() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON) .getSchema(), "request"); IllegalArgumentException throwable = Assertions.catchThrowableOfType(() -> protoGenerator.convert(), IllegalArgumentException.class); assertEquals("Failed to create schema request. May be cyclic object.", throwable.getMessage()); } } //CHECKSTYLE:ON ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/CyclicInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema.model; public class CyclicInfo { private String name; private CyclicInfo child; public String getName() { return name; } public void setName(String name) { this.name = name; } public CyclicInfo getChild() { return child; } public void setChild(CyclicInfo child) { this.child = child; } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/DeptInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema.model; import java.util.List; public class DeptInfo { private String name; private String code; private List scores; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public List getScores() { return scores; } public void setScores(List scores) { this.scores = scores; } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/SchemaService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema.model; import java.util.List; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/schemaService") public class SchemaService { @PostMapping("testUserInfo") public void testUserInfo(@RequestBody UserInfo request) { } @PostMapping("testListType") public void testListType(@RequestBody List request) { } @PostMapping("testCyclic") public void testCyclic(@RequestBody CyclicInfo request) { } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/ScoreInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema.model; public class ScoreInfo { private Integer type; public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } } ================================================ FILE: common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/model/UserInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.codec.protobuf.schema.model; import java.util.List; import java.util.Map; public class UserInfo { private List subDeptInfos; private List> extraInfos; private List>> nestedLists; public List getSubDeptInfos() { return subDeptInfos; } public void setSubDeptInfos(List subDeptInfos) { this.subDeptInfos = subDeptInfos; } public List> getExtraInfos() { return extraInfos; } public void setExtraInfos(List> extraInfos) { this.extraInfos = extraInfos; } public List>> getNestedLists() { return nestedLists; } public void setNestedLists(List>> nestedLists) { this.nestedLists = nestedLists; } } ================================================ FILE: common/common-protobuf/src/test/proto/ModelProtobuf.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; package proto; option java_package = "io.protostuff.runtime.model"; option java_outer_classname = "ModelProtobuf"; message User{ string name = 1; } message RequestHeader{ string destMicroservice = 1; int32 msgType = 2; int32 flags = 3; string schemaId = 5; string operationName = 6; map cseContext = 7; map userMap = 8; repeated string list = 9; repeated User userList = 10; } ================================================ FILE: common/common-protobuf/src/test/resources/ProtoSchema.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; import "google/protobuf/empty.proto"; import "google/protobuf/any.proto"; package a.b; message Empty { } message FieldNeedWrap { repeated ListUser listListUser = 1; repeated MapUser listMapUser = 2; map mapListUser = 3; map mapMapUser = 4; repeated ListListUser listListListUser = 5; repeated ListMapUser listListMapUser = 6; repeated MapListUser listMapListUser = 7; repeated MapMapUser listMapMapUser = 8; map mapMapListUser = 9; map mapMapMapUser = 10; } message User { string name = 1; repeated User friends = 2; } message Ref1 { Ref2 ref = 1; } message Ref2 { Ref1 ref = 1; } //@WrapArguments message BaseRequestWrap { bool boolValue = 1; sint32 iValue = 2; sint64 lValue = 3; float fValue = 4; double dValue = 5; string sValue = 6; repeated sint32 iArray = 7; Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 color = 8; int64 localDate = 9; int64 date = 10; Empty empty = 11; } //@WrapProperty message BaseResponseWrap200 { sint32 value = 1; } //@WrapProperty message BaseResponseWrap444 { Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 value = 1; } //@WrapArguments message BytesRequestWrap { bytes value = 1; } //@WrapProperty message BytesResponseWrap200 { bytes value = 1; } //@WrapArguments message ColorBodyRequestWrap { Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 color = 1; } //@WrapProperty message ColorBodyResponseWrap200 { Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 value = 1; } //@WrapArguments message ListListListStringRequestWrap { repeated ListListString value = 1; } //@WrapProperty message ListListListStringResponseWrap200 { repeated ListListString value = 1; } //@WrapArguments message ListListMapStringRequestWrap { repeated ListMapString value = 1; } //@WrapProperty message ListListMapStringResponseWrap200 { repeated ListMapString value = 1; } //@WrapArguments message ListListStringRequestWrap { repeated ListString value = 1; } //@WrapProperty message ListListStringResponseWrap200 { repeated ListString value = 1; } //@WrapArguments message ListListUserRequestWrap { repeated ListUser value = 1; } //@WrapProperty message ListListUserResponseWrap200 { repeated ListUser value = 1; } //@WrapArguments message ListMapListStringRequestWrap { repeated MapListString value = 1; } //@WrapProperty message ListMapListStringResponseWrap200 { repeated MapListString value = 1; } //@WrapArguments message ListMapMapStringRequestWrap { repeated MapMapString value = 1; } //@WrapProperty message ListMapMapStringResponseWrap200 { repeated MapMapString value = 1; } //@WrapArguments message ListMapStringRequestWrap { repeated MapString value = 1; } //@WrapProperty message ListMapStringResponseWrap200 { repeated MapString value = 1; } //@WrapArguments message ListMapUserRequestWrap { repeated MapUser value = 1; } //@WrapProperty message ListMapUserResponseWrap200 { repeated MapUser value = 1; } //@WrapArguments message ListObjRequestWrap { repeated google.protobuf.Any objs = 1; } //@WrapProperty message ListObjResponseWrap200 { repeated google.protobuf.Any value = 1; } //@WrapArguments message ListUserRequestWrap { repeated User users = 1; } //@WrapProperty message ListUserResponseWrap200 { repeated User value = 1; } //@WrapArguments message MapListStringRequestWrap { map value = 1; } //@WrapProperty message MapListStringResponseWrap200 { map value = 1; } //@WrapArguments message MapListUserRequestWrap { map value = 1; } //@WrapProperty message MapListUserResponseWrap200 { map value = 1; } //@WrapArguments message MapMapListStringRequestWrap { map value = 1; } //@WrapProperty message MapMapListStringResponseWrap200 { map value = 1; } //@WrapArguments message MapMapMapStringRequestWrap { map value = 1; } //@WrapProperty message MapMapMapStringResponseWrap200 { map value = 1; } //@WrapArguments message MapMapStringRequestWrap { map value = 1; } //@WrapProperty message MapMapStringResponseWrap200 { map value = 1; } //@WrapArguments message MapMapUserRequestWrap { map value = 1; } //@WrapProperty message MapMapUserResponseWrap200 { map value = 1; } //@WrapArguments message MapObjRequestWrap { map objs = 1; } //@WrapProperty message MapObjResponseWrap200 { map value = 1; } //@WrapArguments message MapUserRequestWrap { map users = 1; } //@WrapProperty message MapUserResponseWrap200 { map value = 1; } //@WrapArguments message TestTextPlainRequestWrap { string fieldNeedWrap = 1; } //@WrapProperty message TestTextPlainResponseWrap200 { string value = 1; } //@WrapArguments message UserWrapInProtobufRequestWrap { sint32 ivalue = 1; User user = 2; } //@WrapProperty message ListUser { repeated User value = 1; } //@WrapProperty message MapUser { map value = 1; } //@WrapProperty message ListListUser { repeated ListUser value = 1; } //@WrapProperty message ListMapUser { repeated MapUser value = 1; } //@WrapProperty message MapListUser { map value = 1; } //@WrapProperty message MapMapUser { map value = 1; } //@WrapProperty message ListListString { repeated ListString value = 1; } //@WrapProperty message ListMapString { repeated MapString value = 1; } //@WrapProperty message ListString { repeated string value = 1; } //@WrapProperty message MapListString { map value = 1; } //@WrapProperty message MapMapString { map value = 1; } //@WrapProperty message MapString { map value = 1; } enum Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 { RED = 0; YELLOW = 1; BLUE = 2; } service MainService { //@Rpc{"argTypeName":"BaseRequestWrap","responses":{"200":{"typeName":"BaseResponseWrap200"},"444":{"typeName":"BaseResponseWrap444"}}} rpc base (BaseRequestWrap) returns (BaseResponseWrap200); //@Rpc{"argTypeName":"BytesRequestWrap","responses":{"200":{"typeName":"BytesResponseWrap200"}}} rpc bytes (BytesRequestWrap) returns (BytesResponseWrap200); //@Rpc{"argTypeName":"ColorBodyRequestWrap","responses":{"200":{"typeName":"ColorBodyResponseWrap200"}}} rpc colorBody (ColorBodyRequestWrap) returns (ColorBodyResponseWrap200); //@Rpc{"argTypeName":"FieldNeedWrap","responses":{"200":{"typeName":"FieldNeedWrap"}}} rpc fieldNeedWrap (FieldNeedWrap) returns (FieldNeedWrap); //@Rpc{"argTypeName":"ListListListStringRequestWrap","responses":{"200":{"typeName":"ListListListStringResponseWrap200"}}} rpc listListListString (ListListListStringRequestWrap) returns (ListListListStringResponseWrap200); //@Rpc{"argTypeName":"ListListMapStringRequestWrap","responses":{"200":{"typeName":"ListListMapStringResponseWrap200"}}} rpc listListMapString (ListListMapStringRequestWrap) returns (ListListMapStringResponseWrap200); //@Rpc{"argTypeName":"ListListStringRequestWrap","responses":{"200":{"typeName":"ListListStringResponseWrap200"}}} rpc listListString (ListListStringRequestWrap) returns (ListListStringResponseWrap200); //@Rpc{"argTypeName":"ListListUserRequestWrap","responses":{"200":{"typeName":"ListListUserResponseWrap200"}}} rpc listListUser (ListListUserRequestWrap) returns (ListListUserResponseWrap200); //@Rpc{"argTypeName":"ListMapListStringRequestWrap","responses":{"200":{"typeName":"ListMapListStringResponseWrap200"}}} rpc listMapListString (ListMapListStringRequestWrap) returns (ListMapListStringResponseWrap200); //@Rpc{"argTypeName":"ListMapMapStringRequestWrap","responses":{"200":{"typeName":"ListMapMapStringResponseWrap200"}}} rpc listMapMapString (ListMapMapStringRequestWrap) returns (ListMapMapStringResponseWrap200); //@Rpc{"argTypeName":"ListMapStringRequestWrap","responses":{"200":{"typeName":"ListMapStringResponseWrap200"}}} rpc listMapString (ListMapStringRequestWrap) returns (ListMapStringResponseWrap200); //@Rpc{"argTypeName":"ListMapUserRequestWrap","responses":{"200":{"typeName":"ListMapUserResponseWrap200"}}} rpc listMapUser (ListMapUserRequestWrap) returns (ListMapUserResponseWrap200); //@Rpc{"argTypeName":"ListObjRequestWrap","responses":{"200":{"typeName":"ListObjResponseWrap200"}}} rpc listObj (ListObjRequestWrap) returns (ListObjResponseWrap200); //@Rpc{"argTypeName":"ListUserRequestWrap","responses":{"200":{"typeName":"ListUserResponseWrap200"}}} rpc listUser (ListUserRequestWrap) returns (ListUserResponseWrap200); //@Rpc{"argTypeName":"MapListStringRequestWrap","responses":{"200":{"typeName":"MapListStringResponseWrap200"}}} rpc mapListString (MapListStringRequestWrap) returns (MapListStringResponseWrap200); //@Rpc{"argTypeName":"MapListUserRequestWrap","responses":{"200":{"typeName":"MapListUserResponseWrap200"}}} rpc mapListUser (MapListUserRequestWrap) returns (MapListUserResponseWrap200); //@Rpc{"argTypeName":"MapMapListStringRequestWrap","responses":{"200":{"typeName":"MapMapListStringResponseWrap200"}}} rpc mapMapListString (MapMapListStringRequestWrap) returns (MapMapListStringResponseWrap200); //@Rpc{"argTypeName":"MapMapMapStringRequestWrap","responses":{"200":{"typeName":"MapMapMapStringResponseWrap200"}}} rpc mapMapMapString (MapMapMapStringRequestWrap) returns (MapMapMapStringResponseWrap200); //@Rpc{"argTypeName":"MapMapStringRequestWrap","responses":{"200":{"typeName":"MapMapStringResponseWrap200"}}} rpc mapMapString (MapMapStringRequestWrap) returns (MapMapStringResponseWrap200); //@Rpc{"argTypeName":"MapMapUserRequestWrap","responses":{"200":{"typeName":"MapMapUserResponseWrap200"}}} rpc mapMapUser (MapMapUserRequestWrap) returns (MapMapUserResponseWrap200); //@Rpc{"argTypeName":"MapObjRequestWrap","responses":{"200":{"typeName":"MapObjResponseWrap200"}}} rpc mapObj (MapObjRequestWrap) returns (MapObjResponseWrap200); //@Rpc{"argTypeName":"MapUserRequestWrap","responses":{"200":{"typeName":"MapUserResponseWrap200"}}} rpc mapUser (MapUserRequestWrap) returns (MapUserResponseWrap200); //@Rpc{"argTypeName":"google.protobuf.Empty","responses":{"200":{"typeName":"google.protobuf.Empty"}}} rpc noParamVoid (google.protobuf.Empty) returns (google.protobuf.Empty); //@Rpc{"argTypeName":"google.protobuf.Any","responses":{"200":{"typeName":"google.protobuf.Any"}}} rpc obj (google.protobuf.Any) returns (google.protobuf.Any); //@Rpc{"argTypeName":"Ref1","responses":{"200":{"typeName":"Ref2"}}} rpc ref (Ref1) returns (Ref2); //@Rpc{"argTypeName":"TestTextPlainRequestWrap","responses":{"200":{"typeName":"TestTextPlainResponseWrap200"}}} rpc testTextPlain (TestTextPlainRequestWrap) returns (TestTextPlainResponseWrap200); //@Rpc{"argTypeName":"User","responses":{"200":{"typeName":"User"}}} rpc user (User) returns (User); //@Rpc{"argTypeName":"UserWrapInProtobufRequestWrap","responses":{"200":{"typeName":"User"}}} rpc userWrapInProtobuf (UserWrapInProtobufRequestWrap) returns (User); } ================================================ FILE: common/common-rest/pom.xml ================================================ 4.0.0 org.apache.servicecomb common 3.4.0-SNAPSHOT common-rest Java Chassis::Common::Rest org.apache.servicecomb java-chassis-core org.apache.servicecomb common-protobuf io.vertx vertx-codegen provided org.apache.servicecomb registry-local test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb swagger-generator-jaxrs test org.mockito mockito-inline test ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import java.util.List; import org.apache.servicecomb.common.rest.codec.query.QueryCodec; import org.apache.servicecomb.common.rest.codec.query.QueryCodecCsv; import org.apache.servicecomb.common.rest.codec.query.QueryCodecMulti; import org.apache.servicecomb.common.rest.codec.query.QueryCodecPipes; import org.apache.servicecomb.common.rest.codec.query.QueryCodecSsv; import org.apache.servicecomb.common.rest.codec.query.QueryCodecs; import org.apache.servicecomb.common.rest.codec.query.QueryCodecsUtils; import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; import org.apache.servicecomb.common.rest.filter.inner.WebSocketServerCodecFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CommonRestConfiguration { @Bean public QueryCodecCsv scbQueryCodecCsv() { return new QueryCodecCsv(); } @Bean public QueryCodecSsv scbQueryCodecSsv() { return new QueryCodecSsv(); } @Bean public QueryCodecPipes scbQueryCodecPipes() { return new QueryCodecPipes(); } @Bean public QueryCodecMulti scbQueryCodecMulti() { return new QueryCodecMulti(); } @Bean public QueryCodecsUtils scbQueryCodecsUtils(QueryCodecs queryCodecs) { return new QueryCodecsUtils(queryCodecs); } @Bean public RestServerCodecFilter scbRestServerCodecFilter() { return new RestServerCodecFilter(); } @Bean public WebSocketServerCodecFilter scbWebSocketServerCodecFilter() { return new WebSocketServerCodecFilter(); } @Bean public QueryCodecs scbQueryCodecs(List orderedCodecs) { return new QueryCodecs(orderedCodecs); } @Bean public RestEngineSchemaListener scbRestEngineSchemaListener() { return new RestEngineSchemaListener(); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.ServerWebSocket; public class EdgeServerWebSocketInvocationCreator extends ProviderServerWebSocketInvocationCreator { private final String microserviceName; protected MicroserviceReferenceConfig microserviceReferenceConfig; protected final String path; public EdgeServerWebSocketInvocationCreator(String microserviceName, String path, Endpoint endpoint, ServerWebSocket webSocket) { super(null, endpoint, webSocket); this.microserviceName = microserviceName; this.path = path; } @Override public CompletableFuture createAsync() { return createMicroserviceReferenceConfig() .thenCompose(v -> super.createAsync()); } protected CompletableFuture createMicroserviceReferenceConfig() { return SCBEngine.getInstance() .getOrCreateReferenceConfigAsync(microserviceName) .thenAccept(mrc -> { this.microserviceReferenceConfig = mrc; this.microserviceMeta = mrc.getMicroserviceMeta(); }); } @Override protected OperationLocator locateOperation(ServicePathManager servicePathManager) { return servicePathManager.consumerLocateOperation(path, HttpMethod.POST.name()); } @Override protected Invocation createInstance() { ReferenceConfig referenceConfig = microserviceReferenceConfig .createReferenceConfig(restOperationMeta.getOperationMeta()); Invocation invocation = InvocationFactory.forConsumer(referenceConfig, restOperationMeta.getOperationMeta(), restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(), null); invocation.setSync(false); invocation.setEdge(); invocation.setEndpoint(endpoint); // ensure transport name is correct return invocation; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.context.TransportContext; public class HttpTransportContext implements TransportContext { private final HttpServletRequestEx requestEx; private final HttpServletResponseEx responseEx; public HttpTransportContext(HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { this.requestEx = requestEx; this.responseEx = responseEx; } public HttpServletRequestEx getRequestEx() { return requestEx; } public HttpServletResponseEx getResponseEx() { return responseEx; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import io.vertx.core.http.ServerWebSocket; public class ProviderServerWebSocketInvocationCreator extends ServerWebSocketInvocationCreator { public ProviderServerWebSocketInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, ServerWebSocket webSocket) { super(microserviceMeta, endpoint, webSocket); } @Override protected void initTransportContext(Invocation invocation) { WebSocketTransportContext transportContext = new WebSocketTransportContext(websocket); invocation.setTransportContext(transportContext); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.commons.lang3.SystemUtils; public final class RestConst { private RestConst() { } public static final String REST_CLIENT_REQUEST_PATH = "rest-client-request-path"; public static final String SWAGGER_REST_OPERATION = "swaggerRestOperation"; public static final String REST = "rest"; public static final String SCHEME = "cse"; public static final String SCHEME_NEW = "servicecomb"; public static final String URI_PREFIX = SCHEME + "://"; public static final String URI_PREFIX_NEW = SCHEME_NEW + "://"; // in HttpServletRequest attribute public static final String PATH_PARAMETERS = "servicecomb-paths"; // in HttpServletRequest attribute public static final String BODY_PARAMETER = "servicecomb-body"; //in invocation response public static final String INVOCATION_HANDLER_RESPONSE = "servicecomb-invocation-handler-response"; //in invocation response public static final String INVOCATION_HANDLER_PROCESSOR = "servicecomb-invocation-handler-processor"; //in invocation response public static final String INVOCATION_HANDLER_REQUESTCLIENT = "servicecomb-invocation-handler-requestclient"; public static final String REST_PRODUCER_INVOCATION = "servicecomb-rest-producer-invocation"; public static final String REST_INVOCATION_CONTEXT = "servicecomb-rest-invocation-context"; public static final String REST_REQUEST = "servicecomb-rest-request"; public static final String CONSUMER_HEADER = "servicecomb-rest-consumer-header"; public static final String READ_STREAM_PART = "servicecomb-readStreamPart"; public static final String UPLOAD_DIR = "servicecomb.uploads.directory"; public static final String UPLOAD_DEFAULT_DIR = SystemUtils.JAVA_IO_TMPDIR; // limit of one upload file, only available for servlet rest transport public static final String UPLOAD_MAX_FILE_SIZE = "servicecomb.uploads.maxFileSize"; // limit of upload request body public static final String UPLOAD_MAX_SIZE = "servicecomb.uploads.maxSize"; // the size threshold after which files will be written to disk // only available for servlet rest transport public static final String UPLOAD_FILE_SIZE_THRESHOLD = "servicecomb.uploads.fileSizeThreshold"; public static final String PROVIDER_SCAN_REST_CONTROLLER = "servicecomb.provider.rest.scanRestController"; public static final String PRINT_CODEC_ERROR_MESSGAGE = "servicecomb.codec.printErrorMessage"; public static final String HEADER_CONTEXT_MAPPER = "servicecomb.context.headerContextMapper"; public static final String QUERY_CONTEXT_MAPPER = "servicecomb.context.queryContextMapper"; public static final String DECODE_INVOCATION_CONTEXT = "servicecomb.context.decodeInvocationContext"; } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestEngineSchemaListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine.CreateMicroserviceMetaEvent; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; import com.google.common.eventbus.Subscribe; public class RestEngineSchemaListener implements BootListener { @Override public int getOrder() { return -10000; } @Override public void onBeforeRegistry(BootEvent event) { createServicePathManager(event.getScbEngine().getProducerMicroserviceMeta()) .buildProducerPaths(); event.getScbEngine().getEventBus().register(this); } @EnableExceptionPropagation @Subscribe public void onCreateMicroserviceMetaEvent(CreateMicroserviceMetaEvent event) { MicroserviceMeta microserviceMeta = event.getMicroserviceMeta(); createServicePathManager(microserviceMeta); } private ServicePathManager createServicePathManager(MicroserviceMeta microserviceMeta) { // already connect ServicePathManager and MicroserviceMeta instance // no need to save ServicePathManager instance again return new ServicePathManager(microserviceMeta); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.config.YAMLUtil; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.json.Json; public abstract class RestProducerInvocationCreator implements InvocationCreator { private static final Logger LOGGER = LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class); protected MicroserviceMeta microserviceMeta; protected final Endpoint endpoint; protected final HttpServletRequestEx requestEx; protected final HttpServletResponseEx responseEx; protected RestOperationMeta restOperationMeta; protected ProduceProcessor produceProcessor; public RestProducerInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { this.microserviceMeta = microserviceMeta; this.endpoint = endpoint; this.requestEx = requestEx; this.responseEx = responseEx; } @Override public CompletableFuture createAsync() { initRestOperation(); Invocation invocation = createInstance(); initInvocationContext(invocation); addParameterContext(invocation); initTransportContext(invocation); invocation.addLocalContext(RestConst.REST_REQUEST, requestEx); return CompletableFuture.completedFuture(invocation); } protected Invocation createInstance() { return InvocationFactory.forProvider(endpoint, restOperationMeta.getOperationMeta(), null); } protected void initInvocationContext(Invocation invocation) { if (!LegacyPropertyFactory.getBooleanProperty(RestConst.DECODE_INVOCATION_CONTEXT, true)) { return; } String strCseContext = requestEx.getHeader(CoreConst.CSE_CONTEXT); if (StringUtils.isEmpty(strCseContext)) { return; } @SuppressWarnings("unchecked") Map invocationContext = Json.decodeValue(strCseContext, Map.class); invocation.mergeContext(invocationContext); } protected void addParameterContext(Invocation invocation) { String headerContextMapper = LegacyPropertyFactory .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER); String queryContextMapper = LegacyPropertyFactory .getStringProperty(RestConst.QUERY_CONTEXT_MAPPER); Map headerContextMappers; if (headerContextMapper != null) { headerContextMappers = YAMLUtil.yaml2Properties(headerContextMapper); } else { headerContextMappers = new HashMap<>(); } Map queryContextMappers; if (queryContextMapper != null) { queryContextMappers = YAMLUtil.yaml2Properties(queryContextMapper); } else { queryContextMappers = new HashMap<>(); } headerContextMappers.forEach((k, v) -> { if (v instanceof String && requestEx.getHeader(k) != null) { invocation.addContext((String) v, requestEx.getHeader(k)); } }); queryContextMappers.forEach((k, v) -> { if (v instanceof String && requestEx.getParameter(k) != null) { invocation.addContext((String) v, requestEx.getParameter(k)); } }); } protected abstract void initTransportContext(Invocation invocation); protected void initRestOperation() { OperationLocator locator = locateOperation(microserviceMeta); requestEx.setAttribute(RestConst.PATH_PARAMETERS, locator.getPathVarMap()); restOperationMeta = locator.getOperation(); } protected OperationLocator locateOperation(MicroserviceMeta microserviceMeta) { ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); if (servicePathManager == null) { LOGGER.error("No schema defined for {}:{}.", this.microserviceMeta.getAppId(), this.microserviceMeta.getMicroserviceName()); throw Exceptions.create(NOT_FOUND, NOT_DEFINED_ANY_SCHEMA, NOT_FOUND.getReasonPhrase()); } return locateOperation(servicePathManager); } protected OperationLocator locateOperation(ServicePathManager servicePathManager) { return servicePathManager.producerLocateOperation(requestEx.getRequestURI(), requestEx.getMethod()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RestProducerInvocationFlow extends ProducerInvocationFlow { private static final Logger LOGGER = LoggerFactory.getLogger(RestProducerInvocationFlow.class); private static final ProduceProcessor DEFAULT_PRODUCE_PROCESSOR = ProduceProcessorManager.INSTANCE .findDefaultProcessor(); public RestProducerInvocationFlow(InvocationCreator invocationCreator, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { super(invocationCreator, requestEx, responseEx); } @Override protected Invocation sendCreateInvocationException(Throwable throwable) { try { Response response = Exceptions.toProducerResponse(null, throwable); RestServerCodecFilter.encodeResponse(null, response, DEFAULT_PRODUCE_PROCESSOR, responseEx); } catch (Throwable e) { LOGGER.error("Failed to send response when prepare invocation failed, request uri:{}", requestEx.getRequestURI(), e); } endResponse(null); return null; } @Override protected void endResponse(Invocation invocation, Response response) { invocation.getInvocationStageTrace().startProviderSendResponse(); endResponse(invocation); } private void endResponse(Invocation invocation) { try { responseEx.endResponse(); } catch (Throwable flushException) { LOGGER.error("Failed to flush rest response, operation:{}, request uri:{}", invocation == null ? "NA" : invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), flushException); } try { requestEx.getAsyncContext().complete(); } catch (Throwable completeException) { LOGGER.error("Failed to complete async rest response, operation:{}, request uri:{}", invocation == null ? "NA" : invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), completeException); } if (invocation != null) { invocation.getInvocationStageTrace().finishProviderSendResponse(); } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import io.vertx.ext.web.RoutingContext; public class RestVertxProducerInvocationCreator extends RestProducerInvocationCreator { private final RoutingContext routingContext; public RestVertxProducerInvocationCreator(RoutingContext routingContext, MicroserviceMeta microserviceMeta, Endpoint endpoint, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { super(microserviceMeta, endpoint, requestEx, responseEx); this.routingContext = routingContext; } @Override protected void initTransportContext(Invocation invocation) { VertxHttpTransportContext transportContext = new VertxHttpTransportContext(routingContext, requestEx, responseEx); invocation.setTransportContext(transportContext); routingContext.put(RestConst.REST_INVOCATION_CONTEXT, invocation); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.config.YAMLUtil; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.ServerWebSocket; import io.vertx.core.json.Json; public abstract class ServerWebSocketInvocationCreator implements InvocationCreator { private static final Logger LOGGER = LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class); protected MicroserviceMeta microserviceMeta; protected final Endpoint endpoint; protected final ServerWebSocket websocket; protected RestOperationMeta restOperationMeta; public ServerWebSocketInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, ServerWebSocket websocket) { this.microserviceMeta = microserviceMeta; this.endpoint = endpoint; this.websocket = websocket; } @Override public CompletableFuture createAsync() { initRestOperation(); Invocation invocation = createInstance(); initInvocationContext(invocation); addParameterContext(invocation); initTransportContext(invocation); return CompletableFuture.completedFuture(invocation); } protected Invocation createInstance() { return InvocationFactory.forProvider(endpoint, restOperationMeta.getOperationMeta(), null); } protected void initInvocationContext(Invocation invocation) { if (!LegacyPropertyFactory.getBooleanProperty(RestConst.DECODE_INVOCATION_CONTEXT, true)) { return; } String strCseContext = websocket.headers().get(CoreConst.CSE_CONTEXT); if (StringUtils.isEmpty(strCseContext)) { return; } @SuppressWarnings("unchecked") Map invocationContext = Json.decodeValue(strCseContext, Map.class); invocation.mergeContext(invocationContext); } // No queries for websocket protected void addParameterContext(Invocation invocation) { String headerContextMapper = LegacyPropertyFactory .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER); Map headerContextMappers; if (headerContextMapper != null) { headerContextMappers = YAMLUtil.yaml2Properties(headerContextMapper); } else { headerContextMappers = new HashMap<>(); } headerContextMappers.forEach((k, v) -> { if (v instanceof String && websocket.headers().get(k) != null) { invocation.addContext((String) v, websocket.headers().get(k)); } }); } protected abstract void initTransportContext(Invocation invocation); protected void initRestOperation() { OperationLocator locator = locateOperation(microserviceMeta); restOperationMeta = locator.getOperation(); } protected OperationLocator locateOperation(MicroserviceMeta microserviceMeta) { ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); if (servicePathManager == null) { LOGGER.error("No schema defined for {}:{}.", this.microserviceMeta.getAppId(), this.microserviceMeta.getMicroserviceName()); throw Exceptions.create(NOT_FOUND, NOT_DEFINED_ANY_SCHEMA, NOT_FOUND.getReasonPhrase()); } return locateOperation(servicePathManager); } protected OperationLocator locateOperation(ServicePathManager servicePathManager) { return servicePathManager.producerLocateOperation(websocket.path(), HttpMethod.POST.name()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/UploadConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.springframework.core.env.Environment; import jakarta.servlet.MultipartConfigElement; public class UploadConfig { private final Environment environment; public UploadConfig(Environment environment) { this.environment = environment; } /** * null means not support upload */ public String getLocation() { return environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR); } /** * limit of one upload file, only available for servlet rest transport */ public long getMaxFileSize() { return environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L); } /** * limit of upload request body */ public long getMaxSize() { return environment.getProperty(RestConst.UPLOAD_MAX_SIZE, long.class, -1L); } /** * the size threshold after which files will be written to disk, only available for servlet rest transport */ public int getFileSizeThreshold() { return environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0); } public MultipartConfigElement toMultipartConfigElement() { return new MultipartConfigElement( getLocation(), getMaxFileSize(), getMaxSize(), getFileSizeThreshold()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.ext.web.RoutingContext; public class VertxHttpTransportContext extends HttpTransportContext implements VertxTransportContext { private final RoutingContext routingContext; private final Context vertxContext; public VertxHttpTransportContext(RoutingContext routingContext, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { super(requestEx, responseEx); this.routingContext = routingContext; this.vertxContext = Vertx.currentContext(); } public RoutingContext getRoutingContext() { return routingContext; } @Override public Context getVertxContext() { return vertxContext; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.swagger.invocation.context.TransportContext; import io.vertx.core.http.ServerWebSocket; public class WebSocketTransportContext implements TransportContext { private final ServerWebSocket serverWebSocket; public WebSocketTransportContext(ServerWebSocket serverWebSocket) { this.serverWebSocket = serverWebSocket; } public ServerWebSocket getServerWebSocket() { return this.serverWebSocket; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; /** * vertx的HttpClientRequest没有getHeader的能力 * 在写cookie参数时,没办法多次添加cookie,所以只能进行接口包装 */ public interface RestClientRequest { void write(Buffer bodyBuffer); Future end(); void addCookie(String name, String value); void putHeader(String name, String value); MultiMap getHeaders(); void addForm(String name, Object value); Buffer getBodyBuffer() throws Exception; void attach(String name, Object partOrList); HttpClientRequest getHttpClientRequest(); } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public final class RestCodec { private RestCodec() { } public static void argsToRest(Map args, RestOperationMeta restOperation, RestClientRequest clientRequest) throws Exception { int paramSize = restOperation.getParamList().size(); if (paramSize == 0) { return; } for (int idx = 0; idx < paramSize; idx++) { RestParam param = restOperation.getParamList().get(idx); param.getParamProcessor().setValue(clientRequest, args.get(param.getParamName())); } } public static Map restToArgs(HttpServletRequest request, RestOperationMeta restOperation) throws InvocationException { List paramList = restOperation.getParamList(); Map paramValues = new HashMap<>(); for (RestParam param : paramList) { try { paramValues.put(param.getParamName(), param.getParamProcessor().getValue(request)); } catch (Exception e) { String message = String .format("Parameter is not valid for operation [%s]. Parameter is [%s]. Processor is [%s]. Message is [%s].", restOperation.getOperationMeta().getMicroserviceQualifiedName(), param.getParamName(), param.getParamProcessor().getProcessorType(), e.getMessage()); throw new InvocationException(Status.BAD_REQUEST, new CommonExceptionData(message), e); } } return paramValues; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec; import java.util.List; import org.apache.servicecomb.foundation.common.utils.AbstractRestObjectMapper; import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; /** * Manage RestObjectMapper instances. Give users an option to specify custom mappers. */ public class RestObjectMapperFactory { private static AbstractRestObjectMapper defaultMapper = new RestObjectMapper(); private static final AbstractRestObjectMapper viewMapper = new RestObjectMapper(); private static AbstractRestObjectMapper consumerWriterMapper = defaultMapper; static { registerModules(defaultMapper); registerModules(viewMapper); } private static void registerModules(ObjectMapper mapper) { // not use mapper.findAndRegisterModules() // because we need to sort modules, so that customers can override our default module List modules = SPIServiceUtils.getOrLoadSortedService(Module.class); mapper.registerModules(modules.toArray(new Module[0])); } public static AbstractRestObjectMapper getConsumerWriterMapper() { return consumerWriterMapper; } public static AbstractRestObjectMapper getRestObjectMapper() { return defaultMapper; } public static AbstractRestObjectMapper getRestViewMapper() { return viewMapper; } public static void setConsumerWriterMapper(AbstractRestObjectMapper customMapper) { registerModules(customMapper); consumerWriterMapper = customMapper; } public static void setDefaultRestObjectMapper(AbstractRestObjectMapper customMapper) { registerModules(customMapper); defaultMapper = customMapper; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; import jakarta.servlet.http.HttpServletRequest; public interface HeaderCodec { static String encodeValue(Object value) throws UnsupportedEncodingException { return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name()); } // can not be replaced by value.toString() because of date serialize static String convertToString(Object value) throws Exception { return RestObjectMapperFactory.getRestObjectMapper().convertToString(value); } String getCodecName(); void encode(RestClientRequest clientRequest, String name, Object value) throws Exception; Object decode(HeaderProcessor processor, HttpServletRequest request); } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; public class HeaderCodecCsv extends HeaderCodecWithDelimiter { public static final String CODEC_NAME = "form:0"; public static final String DELIMITER = ","; public HeaderCodecCsv() { super(CODEC_NAME, DELIMITER, DELIMITER); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public class HeaderCodecMulti implements HeaderCodec { public static final String NAME = "form:1"; @Override public String getCodecName() { return NAME; } @Override public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { if (null == value) { // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. return; } if (!(value instanceof Collection)) { throw new InvocationException(Status.BAD_REQUEST, new CommonExceptionData("Array type of header should be Collection")); } for (Object item : ((Collection) value)) { clientRequest.putHeader(name, RestObjectMapperFactory.getConsumerWriterMapper().convertToString(item)); } } @Override public Object decode(HeaderProcessor processor, HttpServletRequest request) { Enumeration headerValues = request.getHeaders(processor.getParameterPath()); if (headerValues == null) { //Even if the paramPath does not exist, headerValues won't be null at now return null; } return processor.convertValue(Collections.list(headerValues), processor.getTargetType()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; public class HeaderCodecPipes extends HeaderCodecWithDelimiter { public static final String CODEC_NAME = "pipeDelimited:0"; public static final String JOIN_DELIMITER = "|"; public static final String SPLIT_DELIMITER = "\\|"; public HeaderCodecPipes() { super(CODEC_NAME, JOIN_DELIMITER, SPLIT_DELIMITER); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; import jakarta.servlet.http.HttpServletRequest; public class HeaderCodecSimple implements HeaderCodec { public static final String NAME = "simple"; @Override public String getCodecName() { return NAME; } @Override public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { if (null == value) { // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. return; } clientRequest.putHeader(name, RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value)); } @Override public Object decode(HeaderProcessor processor, HttpServletRequest request) { Object value = request.getHeader(processor.getParameterPath()); if (value == null) { value = processor.checkRequiredAndDefaultValue(); } return processor.convertValue(value, processor.getTargetType()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; public class HeaderCodecSsv extends HeaderCodecWithDelimiter { public static final String CODEC_NAME = "spaceDelimited:0"; public static final String DELIMITER = " "; public HeaderCodecSsv() { super(CODEC_NAME, DELIMITER, DELIMITER); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; import java.util.Collection; import java.util.List; import java.util.StringJoiner; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public abstract class HeaderCodecWithDelimiter implements HeaderCodec { private final String name; private final String joinDelimiter; private final String splitDelimiter; public HeaderCodecWithDelimiter(String name, String joinDelimiter, String splitDelimiter) { this.name = name; this.joinDelimiter = joinDelimiter; this.splitDelimiter = splitDelimiter; } @Override public String getCodecName() { return name; } @Override public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { if (null == value) { // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. return; } if (!(value instanceof Collection)) { throw new InvocationException(Status.BAD_REQUEST, new CommonExceptionData("Array type of header should be Collection")); } clientRequest.putHeader(name, join((Collection) value)); } protected String join(Collection values) throws Exception { StringJoiner joiner = new StringJoiner(joinDelimiter); for (Object value : values) { String strValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value); joiner.add(strValue); } return joiner.toString(); } @Override public Object decode(HeaderProcessor processor, HttpServletRequest request) { String headerValues = request.getHeader(processor.getParameterPath()); if (headerValues == null) { headerValues = (String) processor.checkRequiredAndDefaultValue(); } return processor.convertValue(List.of(headerValues.split(splitDelimiter)), processor.getTargetType()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.header; import java.util.HashMap; import java.util.Map; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; public class HeaderCodecsUtils { private static final Map CODECS; static { CODECS = new HashMap<>(); CODECS.put(HeaderCodecSimple.NAME, new HeaderCodecSimple()); CODECS.put(HeaderCodecMulti.NAME, new HeaderCodecMulti()); CODECS.put(HeaderCodecCsv.CODEC_NAME, new HeaderCodecCsv()); CODECS.put(HeaderCodecPipes.CODEC_NAME, new HeaderCodecPipes()); CODECS.put(HeaderCodecSsv.CODEC_NAME, new HeaderCodecSsv()); } private HeaderCodecsUtils() { } public static HeaderCodec find(Parameter.StyleEnum styleEnum, Boolean explode) { return CODECS.get(formatName(styleEnum, explode)); } private static String formatName(StyleEnum styleEnum, Boolean explode) { if (styleEnum == null) { return HeaderCodecSimple.NAME; } return styleEnum + ":" + (explode != null && explode ? "1" : "0"); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/AbstractParamProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import com.fasterxml.jackson.databind.JavaType; import com.google.common.base.Defaults; public abstract class AbstractParamProcessor implements ParamValueProcessor { protected String paramPath; // for consumer, targetType should be null protected JavaType targetType; protected Object defaultValue; protected boolean required = false; public Object getDefaultValue() { return defaultValue; } public AbstractParamProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) { this.paramPath = paramPath; this.targetType = targetType; this.defaultValue = defaultValue; this.required = required; if (defaultValue == null && targetType != null && targetType.getRawClass().isPrimitive()) { this.defaultValue = Defaults.defaultValue(targetType.getRawClass()); } } @Override public String getParameterPath() { return paramPath; } public JavaType getTargetType() { return targetType; } public boolean isRequired() { return required; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.entity.ContentType; import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.type.SimpleType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.parameters.RequestBody; import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.impl.BufferImpl; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; public class BodyProcessorCreator implements ParamValueProcessorCreator { private static final Logger LOGGER = LoggerFactory.getLogger(BodyProcessorCreator.class); public static final String REQUEST_BODY_NAME = "X_REQUEST"; public static final String EXT_ID = "protobuf"; public static final String PARAM_TYPE = "body"; private static final JavaType OBJECT_TYPE = SimpleType.constructUnsafe(Object.class); private static final Object LOCK = new Object(); // This configuration is used for temporary use only. // Do not use it if you are sure how it works. And may be deleted in the future. public static final String PARAM_DECODE_AS_OBJECT = "servicecomb.rest.parameter.decodeAsObject"; public static final String PARAM_DEFAULT_REQUEST_ENCODING = "servicecomb.rest.parameter.default-request-encoding"; private static Boolean decodeAsObject; private static String defaultRequestEncoding; public static class BodyProcessor implements ParamValueProcessor { // Producer target type. For consumer, is null. protected JavaType targetType; protected Class serialViewClass; protected boolean isRequired; protected OpenAPI openAPI; protected ScopedProtobufSchemaManager scopedProtobufSchemaManager; protected List supportedContentTypes = new ArrayList<>(); protected OperationMeta operationMeta; protected RequestBody requestBody; public BodyProcessor(OperationMeta operationMeta, JavaType targetType, RequestBody requestBody) { this.requestBody = requestBody; if (!StringUtils.isEmpty((String) this.requestBody.getExtensions() .get(SwaggerConst.EXT_JSON_VIEW))) { try { this.serialViewClass = Class.forName((String) this.requestBody.getExtensions() .get(SwaggerConst.EXT_JSON_VIEW)); } catch (Throwable e) { //ignore LOGGER.warn("Failed to create body processor {}, annotation @JsonView may be invalid", serialViewClass, e); } } this.targetType = targetType; this.isRequired = this.requestBody.getRequired() != null && this.requestBody.getRequired(); if (this.requestBody.getContent() != null) { supportedContentTypes.addAll(this.requestBody.getContent().keySet()); } if (operationMeta != null) { this.operationMeta = operationMeta; this.openAPI = operationMeta.getSchemaMeta().getSwagger(); if (supportedContentTypes.contains(SwaggerConst.PROTOBUF_TYPE)) { this.scopedProtobufSchemaManager = getOrCreateScopedProtobufSchemaManager( operationMeta.getMicroserviceMeta()); } } } private ScopedProtobufSchemaManager getOrCreateScopedProtobufSchemaManager(MicroserviceMeta microserviceMeta) { ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); if (scopedProtobufSchemaManager == null) { synchronized (LOCK) { scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); if (scopedProtobufSchemaManager == null) { scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); } } } return scopedProtobufSchemaManager; } @Override public Class getSerialViewClass() { return serialViewClass; } @Override public Object getValue(HttpServletRequest request) throws Exception { Object result = getValueImpl(request); if (result == null && this.isRequired) { throw new InvocationException(Status.BAD_REQUEST, "Body parameter is required."); } return result; } private Object getValueImpl(HttpServletRequest request) throws IOException { Object body = request.getAttribute(RestConst.BODY_PARAMETER); if (body != null) { return convertValue(body, targetType); } // edge support convert from form-data or x-www-form-urlencoded to json automatically String contentType = validContentType(request.getContentType()); // support RFC 7231, ignore case for content-type if (StringUtils.equalsIgnoreCase(contentType, MediaType.MULTIPART_FORM_DATA) || StringUtils.equalsIgnoreCase(contentType, MediaType.APPLICATION_FORM_URLENCODED)) { return convertValue(request.getParameterMap(), targetType); } // for standard HttpServletRequest, getInputStream will never return null // but for mocked HttpServletRequest, maybe get a null // like org.apache.servicecomb.provider.springmvc.reference.ClientToHttpServletRequest InputStream inputStream = request.getInputStream(); if (inputStream == null) { return null; } if (StringUtils.equalsIgnoreCase(contentType, MediaType.APPLICATION_JSON)) { try { ObjectReader reader = serialViewClass != null ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) : RestObjectMapperFactory.getRestObjectMapper().reader(); if (decodeAsObject()) { return reader.forType(OBJECT_TYPE).readValue(inputStream); } return reader.forType(targetType == null ? OBJECT_TYPE : targetType) .readValue(inputStream); } catch (MismatchedInputException e) { // there is no way to detect InputStream is empty, so have to catch the exception if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { LOGGER.info("Empty content and required is false, taken as null"); return null; } throw e; } } if (StringUtils.equalsIgnoreCase(contentType, SwaggerConst.PROTOBUF_TYPE)) { ProtoMapper protoMapper = scopedProtobufSchemaManager .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), REQUEST_BODY_NAME, requestBody.getContent().get(SwaggerConst.PROTOBUF_TYPE).getSchema()); RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() .createRootDeserializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), targetType == null ? OBJECT_TYPE : targetType); PropertyWrapper result = deserializer.deserialize(inputStream.readAllBytes()); return result.getValue(); } if (StringUtils.equalsIgnoreCase(contentType, MediaType.TEXT_PLAIN)) { try { if (targetType != null && String.class.equals(targetType.getRawClass())) { return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } ObjectReader reader = serialViewClass != null ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) : RestObjectMapperFactory.getRestObjectMapper().reader(); if (decodeAsObject()) { return reader.forType(OBJECT_TYPE).readValue(inputStream); } return reader.forType(targetType == null ? OBJECT_TYPE : targetType) .readValue(inputStream); } catch (MismatchedInputException e) { // there is no way to detect InputStream is empty, so have to catch the exception if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { LOGGER.info("Empty content and required is false, taken as null"); return null; } throw e; } } throw new IllegalArgumentException(String.format("operation %s not support content-type %s", operationMeta.getSchemaQualifiedName(), contentType)); } private String validContentType(String type) { if (StringUtils.isEmpty(type)) { if (supportedContentTypes.size() == 0) { throw new IllegalArgumentException("operation do not have any content type support."); } if (supportedContentTypes.contains(clientEncodingDefault())) { return clientEncodingDefault(); } return supportedContentTypes.get(0); } ContentType contentType = ContentType.parse(type); return contentType.getMimeType(); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { String userContentType = clientRequest.getHeaders().get(HttpHeaders.CONTENT_TYPE); String contentType = validContentType(userContentType); if (StringUtils.isEmpty(userContentType)) { clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, contentType); } if (arg != null) { Buffer buffer = createBodyBuffer(contentType, arg); clientRequest.write(buffer); } } /** * Serialize body object into body buffer, according to the Content-Type. */ private Buffer createBodyBuffer(String contentType, Object arg) throws IOException { if (MediaType.APPLICATION_JSON.equals(contentType)) { try (BufferOutputStream output = new BufferOutputStream()) { RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); return output.getBuffer(); } } if (SwaggerConst.PROTOBUF_TYPE.equals(contentType)) { ProtoMapper protoMapper = scopedProtobufSchemaManager .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), REQUEST_BODY_NAME, requestBody.getContent().get(SwaggerConst.PROTOBUF_TYPE).getSchema()); RootSerializer serializer = protoMapper.getSerializerSchemaManager() .createRootSerializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), Object.class); Map bodyArg = new HashMap<>(1); bodyArg.put("value", arg); return new BufferImpl().appendBytes(serializer.serialize(bodyArg)); } // For text/plain try (BufferOutputStream output = new BufferOutputStream()) { if (arg instanceof String) { output.write(((String) arg).getBytes(StandardCharsets.UTF_8)); } else { RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); } return output.getBuffer(); } } @Override public String getParameterPath() { return ""; } @Override public String getProcessorType() { return PARAM_TYPE; } } public static class RawJsonBodyProcessor implements ParamValueProcessor { protected JavaType targetType; protected Class serialViewClass; protected boolean isRequired; public RawJsonBodyProcessor(JavaType targetType, boolean isRequired) { this(targetType, null, isRequired); } public RawJsonBodyProcessor(JavaType targetType, String serialViewClass, boolean isRequired) { if (!StringUtils.isEmpty(serialViewClass)) { try { this.serialViewClass = Class.forName(serialViewClass); } catch (Throwable e) { //ignore LOGGER.warn("Failed to create body processor {}, annotation @JsonView may be invalid", serialViewClass, e); } } this.targetType = targetType; this.isRequired = isRequired; } @Override public Object getValue(HttpServletRequest request) throws Exception { Object body = request.getAttribute(RestConst.BODY_PARAMETER); if (body != null) { return convertValue(body, targetType); } InputStream inputStream = request.getInputStream(); if (inputStream == null) { return null; } return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { if (arg instanceof String) { clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); clientRequest.write(Buffer.buffer((String) arg)); return; } throw new IllegalArgumentException("@RawJsonRequestBody only supports string type."); } @Override public String getParameterPath() { return ""; } @Override public String getProcessorType() { return PARAM_TYPE; } } public BodyProcessorCreator() { ParamValueProcessorCreatorManager.INSTANCE.register(PARAM_TYPE, this); } @Override public ParamValueProcessor create(OperationMeta operationMeta, String parameterName, RequestBody parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); boolean rawJson = SwaggerUtils.isRawJsonType(parameter); if (rawJson) { return new RawJsonBodyProcessor(targetType, (String) parameter.getExtensions() .get(SwaggerConst.EXT_JSON_VIEW), parameter.getRequired() != null && parameter.getRequired()); } return new BodyProcessor(operationMeta, targetType, parameter); } private static boolean decodeAsObject() { if (decodeAsObject == null) { decodeAsObject = LegacyPropertyFactory .getBooleanProperty(PARAM_DECODE_AS_OBJECT, false); } return decodeAsObject; } private static String clientEncodingDefault() { if (defaultRequestEncoding == null) { defaultRequestEncoding = LegacyPropertyFactory .getStringProperty(PARAM_DEFAULT_REQUEST_ENCODING, MediaType.APPLICATION_JSON); } return defaultRequestEncoding; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/CookieProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import java.util.Objects; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.parameters.Parameter; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public class CookieProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "cookie"; public static class CookieProcessor extends AbstractParamProcessor { public CookieProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) { super(paramPath, targetType, defaultValue, required); } @Override public Object getValue(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); Object value = null; if (cookies == null || cookies.length == 0) { value = checkRequiredAndDefaultValue(); return convertValue(value, targetType); } for (Cookie cookie : cookies) { if (Objects.equals(paramPath, cookie.getName())) { value = cookie.getValue(); break; } } if (value == null) { value = checkRequiredAndDefaultValue(); } return convertValue(value, targetType); } private Object checkRequiredAndDefaultValue() { if (isRequired()) { throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); } return getDefaultValue(); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { clientRequest.addCookie(paramPath, RestObjectMapperFactory.getConsumerWriterMapper().convertToString(arg)); } @Override public String getProcessorType() { return PARAMTYPE; } } public CookieProcessorCreator() { ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); } @Override public ParamValueProcessor create(OperationMeta operationMeta, String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); return new CookieProcessor(parameterName, targetType, parameter.getSchema().getDefault(), parameter.getRequired() != null && parameter.getRequired()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.converter.Converter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.RequestBody; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.Response.Status; @SuppressWarnings("unchecked") public class FormProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "formData"; public static class FormProcessor extends AbstractParamProcessor { private final boolean repeatedType; public FormProcessor(String paraName, RequestBody formParameter, String mediaType, JavaType targetType) { super(paraName, targetType, formParameter.getContent().get(mediaType).getSchema().getDefault(), formParameter.getRequired() != null && formParameter.getRequired()); this.repeatedType = formParameter.getContent().get(mediaType) .getSchema().getProperties().get(paraName) instanceof ArraySchema; } @Override public Object getValue(HttpServletRequest request) { Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); if (forms != null && !forms.isEmpty()) { return convertValue(forms.get(paramPath), targetType); } if (repeatedType) { //Even if the paramPath does not exist, it won't be null at now return convertValue(request.getParameterValues(paramPath), targetType); } Object value = request.getParameter(paramPath); if (value == null) { value = checkRequiredAndDefaultValue(); } return convertValue(value, targetType); } private Object checkRequiredAndDefaultValue() { if (isRequired()) { throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); } return getDefaultValue(); } @Override public void setValue(RestClientRequest clientRequest, Object arg) { clientRequest.addForm(paramPath, arg); } @Override public String getProcessorType() { return PARAMTYPE; } } public FormProcessorCreator() { ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); } @Override public ParamValueProcessor create(OperationMeta operationMeta, String paramName, RequestBody parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); if (isPart(parameter, paramName)) { return new PartProcessor(paramName, parameter, genericParamType); } String mediaType = SwaggerConst.FORM_MEDIA_TYPE; if (parameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null) { mediaType = SwaggerConst.FILE_MEDIA_TYPE; } return new FormProcessor(paramName, parameter, mediaType, targetType); } private boolean isPart(RequestBody parameter, String paramName) { MediaType file = parameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE); if (file != null) { Schema schema = (Schema) file.getSchema().getProperties().get(paramName); if (schema instanceof ArraySchema) { return "string".equals(schema.getItems().getType()) && "binary".equals(schema.getItems().getFormat()); } else { return ("string".equals(schema.getType()) && "binary".equals(schema.getFormat())); } } return false; } public static class PartProcessor extends AbstractParamProcessor { private static final Type partListType = ParameterizedTypeUtil.make(List.class, Part.class); // key is target type private static final Map partsToTargetConverters = SPIServiceUtils.getSortedService( Converter.class) .stream() .filter(c -> partListType.equals(c.getSrcType())) .collect(Collectors.toMap(Converter::getTargetType, Function.identity())); // key is target type private static final Map partToTargetConverters = SPIServiceUtils.getSortedService(Converter.class) .stream() .filter(c -> c.getSrcType() instanceof Class && Part.class.isAssignableFrom((Class) c.getSrcType())) .collect(Collectors.toMap(Converter::getTargetType, Function.identity())); private final boolean repeatedType; private Converter converter; PartProcessor(String paramName, RequestBody formParameter, Type genericParamType) { super(paramName, null, formParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema().getDefault(), formParameter.getRequired() != null && formParameter.getRequired()); this.repeatedType = formParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) .getSchema().getProperties().get(paramName) instanceof ArraySchema; initConverter(genericParamType); } private void initConverter(Type genericParamType) { if (repeatedType) { initRepeatedConverter(genericParamType); return; } initNormalConverter(genericParamType); } private void initNormalConverter(Type genericParamType) { if (genericParamType instanceof JavaType) { genericParamType = ((JavaType) genericParamType).getRawClass(); } converter = partToTargetConverters.get(genericParamType); } private void initRepeatedConverter(Type genericParamType) { if (genericParamType instanceof JavaType) { genericParamType = ParameterizedTypeUtil.make(((JavaType) genericParamType).getRawClass(), ((JavaType) genericParamType).getContentType()); } converter = partsToTargetConverters.get(genericParamType); } @Override public Object getValue(HttpServletRequest request) throws Exception { if (repeatedType) { // get all parts List parts = request.getParts() .stream() .filter(part -> part.getName().equals(paramPath)) .collect(Collectors.toList()); return convertValue(converter, parts); } return convertValue(converter, request.getPart(paramPath)); } public Object convertValue(Converter converter, Object value) { if (value == null || converter == null) { return value; } return converter.convert(value); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { clientRequest.attach(paramPath, arg); } @Override public String getProcessorType() { return PARAMTYPE; } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.header.HeaderCodec; import org.apache.servicecomb.common.rest.codec.header.HeaderCodecsUtils; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public class HeaderProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "header"; public static class HeaderProcessor extends AbstractParamProcessor { // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. private final boolean ignoreRequiredCheck = LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", false); private final HeaderCodec headerCodec; public HeaderProcessor(HeaderParameter headerParameter, JavaType targetType) { super(headerParameter.getName(), targetType, headerParameter.getSchema().getDefault(), headerParameter.getRequired() != null && headerParameter.getRequired()); if ((headerParameter.getSchema() instanceof ArraySchema) && headerParameter.getStyle() == null) { // compatible to default settings this.headerCodec = HeaderCodecsUtils.find(StyleEnum.FORM, true); } else { this.headerCodec = HeaderCodecsUtils.find(headerParameter.getStyle(), headerParameter.getExplode()); } } @Override public Object getValue(HttpServletRequest request) { return headerCodec.decode(this, request); } public Object checkRequiredAndDefaultValue() { if (!ignoreRequiredCheck && isRequired()) { throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); } return getDefaultValue(); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { headerCodec.encode(clientRequest, paramPath, arg); } @Override public String getProcessorType() { return PARAMTYPE; } } public HeaderProcessorCreator() { ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); } @Override public ParamValueProcessor create(OperationMeta operationMeta, String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); return new HeaderProcessor((HeaderParameter) parameter, targetType); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import com.fasterxml.jackson.databind.JavaType; public interface ParamValueProcessor { Object getValue(HttpServletRequest request) throws Exception; void setValue(RestClientRequest clientRequest, Object arg) throws Exception; default Object convertValue(Object value, JavaType targetType) { if (value == null || targetType == null) { return value; } if (getSerialViewClass() != null) { return RestObjectMapperFactory.getRestViewMapper().setConfig( RestObjectMapperFactory.getRestViewMapper().getDeserializationConfig().withView(getSerialViewClass())) .convertValue(value, targetType); } return RestObjectMapperFactory.getRestObjectMapper() .convertValue(value, targetType); } String getParameterPath(); String getProcessorType(); default Class getSerialViewClass() { return null; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import org.apache.servicecomb.core.definition.OperationMeta; public interface ParamValueProcessorCreator { default ParamValueProcessor create(OperationMeta operationMeta, String paramName, T parameter, Type genericParamType) { throw new IllegalStateException("not implemented"); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.foundation.common.RegisterManager; public final class ParamValueProcessorCreatorManager extends RegisterManager { private static final String NAME = "param value processor mgr"; public static final ParamValueProcessorCreatorManager INSTANCE = new ParamValueProcessorCreatorManager(); static { new PathProcessorCreator(); new QueryProcessorCreator(); new FormProcessorCreator(); new HeaderProcessorCreator(); new CookieProcessorCreator(); new BodyProcessorCreator(); } private ParamValueProcessorCreatorManager() { super(NAME); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/PathProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.definition.OperationMeta; import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.parameters.Parameter; import jakarta.servlet.http.HttpServletRequest; public class PathProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "path"; public static class PathProcessor extends AbstractParamProcessor { public PathProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) { super(paramPath, targetType, defaultValue, required); } @Override public Object getValue(HttpServletRequest request) { @SuppressWarnings("unchecked") Map pathVarMap = (Map) request.getAttribute(RestConst.PATH_PARAMETERS); if (pathVarMap == null) { return null; } String value = pathVarMap.get(paramPath); if (value == null) { return null; } return convertValue(StringUtils.uriDecode(value, StandardCharsets.UTF_8), targetType); } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { // path不需要set } @Override public String getProcessorType() { return PARAMTYPE; } } public PathProcessorCreator() { ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); } @Override public ParamValueProcessor create(OperationMeta operationMeta, String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); return new PathProcessor(parameterName, targetType, parameter.getSchema().getDefault(), true); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.query.QueryCodec; import org.apache.servicecomb.common.rest.codec.query.QueryCodecsUtils; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.QueryParameter; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; @SuppressWarnings("unchecked") public class QueryProcessorCreator implements ParamValueProcessorCreator { public static final String PARAMTYPE = "query"; public static class QueryProcessor extends AbstractParamProcessor { // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. private final boolean emptyAsNull = LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.parameter.query.emptyAsNull", false); // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. private final boolean ignoreDefaultValue = LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", false); // This configuration is used for temporary use only. Do not use it if you are sure how it works. And may be deleted in future. private final boolean ignoreRequiredCheck = LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", false); private final boolean repeatedType; private final QueryCodec queryCodec; public QueryProcessor(QueryParameter queryParameter, JavaType targetType) { super(queryParameter.getName(), targetType, queryParameter.getSchema().getDefault(), queryParameter.getRequired() != null && queryParameter.getRequired()); this.repeatedType = queryParameter.getSchema() instanceof ArraySchema; this.queryCodec = QueryCodecsUtils.find(queryParameter.getStyle(), queryParameter.getExplode()); } @Override public Object getValue(HttpServletRequest request) { return queryCodec.decode(this, request); } public Object getAndCheckParameter(HttpServletRequest request) { Object value = request.getParameter(paramPath); // compatible to SpringMVC @RequestParam. BODY_PARAMETER is only set for SpringMVC. if (value == null) { Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); value = (forms == null || forms.get(paramPath) == null) ? null : forms.get(paramPath); } // make some old systems happy if (emptyAsNull && "".equals(value)) { value = null; } return value != null ? value : checkRequiredAndDefaultValue(); } private Object checkRequiredAndDefaultValue() { if (!ignoreRequiredCheck && isRequired()) { throw new InvocationException(Status.BAD_REQUEST, String.format("Parameter %s is required.", paramPath)); } Object defaultValue = getDefaultValue(); if (!ignoreDefaultValue && defaultValue != null) { return defaultValue; } return null; } @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { // query不需要set } @Override public String getProcessorType() { return PARAMTYPE; } public QueryCodec getQueryCodec() { return queryCodec; } public boolean isRepeatedType() { return repeatedType; } public Object convertValue(Object value) { return convertValue(value, targetType); } } public QueryProcessorCreator() { ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); } @Override public ParamValueProcessor create(OperationMeta operationMeta, String parameterName, Parameter parameter, Type genericParamType) { JavaType targetType = genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); return new QueryProcessor((QueryParameter) parameter, targetType); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import com.fasterxml.jackson.databind.JavaType; import jakarta.ws.rs.core.MediaType; public class ProduceEventStreamProcessor implements ProduceProcessor { private int writeIndex = 0; @Override public String getName() { return MediaType.SERVER_SENT_EVENTS; } @Override public int getOrder() { return 0; } @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { String buffer = "id: " + (writeIndex++) + "\n" + "data: " + RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(result) + "\n\n"; output.write(buffer.getBytes(StandardCharsets.UTF_8)); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { String buffer = new String(input.readAllBytes(), StandardCharsets.UTF_8); for (String line : buffer.split("\n")) { if (line.startsWith("data: ")) { return RestObjectMapperFactory.getRestObjectMapper().readValue(line.substring(5), type); } } return null; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import java.io.InputStream; import java.io.OutputStream; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import com.fasterxml.jackson.databind.JavaType; public class ProduceJsonProcessor implements ProduceProcessor { private Class serializationView; @Override public String getSerializationView() { return serializationView == null ? ProduceProcessor.super.getSerializationView() : serializationView.getName(); } @Override public void setSerializationView(Class serializationView) { if (serializationView == null) { return; } this.serializationView = serializationView; } @Override public String getName() { return MediaType.APPLICATION_JSON; } @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { if (serializationView == null) { RestObjectMapperFactory.getRestObjectMapper().writeValue(output, result); return; } RestObjectMapperFactory.getRestObjectMapper().writerWithView(serializationView).writeValue(output, result); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { if (serializationView == null) { return RestObjectMapperFactory.getRestObjectMapper().readValue(input, type); } return RestObjectMapperFactory.getRestObjectMapper().readerWithView(serializationView) .forType(type).readValue(input); } @Override public int getOrder() { return 0; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import java.io.InputStream; import java.io.OutputStream; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; import com.fasterxml.jackson.databind.JavaType; import io.vertx.core.buffer.Buffer; public interface ProduceProcessor { String getName(); int getOrder(); default String getSerializationView() { return ProduceProcessorManager.DEFAULT_SERIAL_CLASS; } default void setSerializationView(Class serializationView) { // do nothing default } default void encodeResponse(OutputStream output, Object result) throws Exception { if (result == null) { return; } doEncodeResponse(output, result); } void doEncodeResponse(OutputStream output, Object result) throws Exception; default Buffer encodeResponse(Object result) throws Exception { if (null == result) { return null; } try (BufferOutputStream output = new BufferOutputStream()) { doEncodeResponse(output, result); return output.getBuffer(); } } default Object decodeResponse(InputStream input, JavaType type) throws Exception { return doDecodeResponse(input, type); } Object doDecodeResponse(InputStream input, JavaType type) throws Exception; default Object decodeResponse(Buffer buffer, JavaType type) throws Exception { if (buffer.length() == 0) { return null; } try (BufferInputStream input = new BufferInputStream(buffer)) { return doDecodeResponse(input, type); } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.entity.ContentType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.RegisterManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; public final class ProduceProcessorManager extends RegisterManager> { private static final Logger LOGGER = LoggerFactory.getLogger(ProduceProcessorManager.class); private static final List produceProcessor = SPIServiceUtils.getSortedService(ProduceProcessor.class); private static final String NAME = "produce processor mgr"; public static final String DEFAULT_SERIAL_CLASS = "servicecomb_default_class"; public static final ProduceProcessorManager INSTANCE = new ProduceProcessorManager(); public static final String PARAM_DEFAULT_RESPONSE_ENCODING = "servicecomb.rest.parameter.default-response-encoding"; private static String defaultResponseEncoding; private final Map jsonProcessorMap; private final Map plainProcessorMap; private final Map defaultProcessorMap; private ProduceProcessorManager() { super(NAME); produceProcessor.forEach(processor -> { Map prodProcessorMap = getObjMap() .computeIfAbsent(processor.getName(), key -> new HashMap<>()); prodProcessorMap.putIfAbsent(processor.getSerializationView(), processor); }); jsonProcessorMap = ensureFindValue(MediaType.APPLICATION_JSON); plainProcessorMap = ensureFindValue(MediaType.TEXT_PLAIN); defaultProcessorMap = jsonProcessorMap; } private static ProduceProcessor cloneNewProduceProcessor(Class serialViewClass, Map produceViewMap) { ProduceProcessor newInstance; try { newInstance = produceViewMap.get(DEFAULT_SERIAL_CLASS).getClass().getDeclaredConstructor().newInstance(); newInstance.setSerializationView(serialViewClass); return newInstance; } catch (Throwable e) { // ignore exception LOGGER.warn("Failed to create produceProcessor with {}", serialViewClass.getName(), e); } return produceViewMap.get(DEFAULT_SERIAL_CLASS); } private static String defaultResponseEncoding() { if (defaultResponseEncoding == null) { defaultResponseEncoding = LegacyPropertyFactory .getStringProperty(PARAM_DEFAULT_RESPONSE_ENCODING, MediaType.APPLICATION_JSON); } return defaultResponseEncoding; } public ProduceProcessor findJsonProcessorByViewClass(Class serialViewClass) { if (serialViewClass == null) { return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS); } return jsonProcessorMap.computeIfAbsent(serialViewClass.getName(), viewKey -> cloneNewProduceProcessor(serialViewClass, jsonProcessorMap)); } public ProduceProcessor findPlainProcessorByViewClass(Class serialViewClass) { if (serialViewClass == null) { return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); } return plainProcessorMap.computeIfAbsent(serialViewClass.getName(), viewKey -> cloneNewProduceProcessor(serialViewClass, plainProcessorMap)); } public ProduceProcessor findDefaultJsonProcessor() { return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS); } public ProduceProcessor findDefaultProcessor() { return defaultProcessorMap.get(DEFAULT_SERIAL_CLASS); } public ProduceProcessor findDefaultPlainProcessor() { return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); } public ProduceProcessor createProduceProcessor(OperationMeta operationMeta, int statusCode, String accept, Class serialViewClass) { // If no produces defined, using default processor ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); ApiResponse response = responses.get(String.valueOf(statusCode)); if (response == null || response.getContent() == null || response.getContent().size() == 0) { return findDefaultProcessor(); } // check intersection of `Accept` and `Produces` if (accept == null) { if (response.getContent().get(defaultResponseEncoding()) != null) { accept = defaultResponseEncoding(); } else { accept = response.getContent().keySet().iterator().next(); } } String actualAccept = null; for (String item : accept.split(",")) { ContentType contentType = ContentType.parse(item); if (MediaType.WILDCARD.equals(contentType.getMimeType()) || MediaType.MEDIA_TYPE_WILDCARD.equals(contentType.getMimeType())) { if (response.getContent().get(defaultResponseEncoding()) != null) { actualAccept = defaultResponseEncoding(); } else { actualAccept = response.getContent().keySet().iterator().next(); } break; } if (response.getContent().get(contentType.getMimeType()) != null) { actualAccept = contentType.getMimeType(); break; } } if (actualAccept == null) { LOGGER.warn("Operation {} do not support accept type {}", operationMeta.getSchemaQualifiedName(), accept); return findDefaultProcessor(); } if (MediaType.APPLICATION_JSON.equals(actualAccept)) { return findJsonProcessorByViewClass(serialViewClass); } if (SwaggerConst.PROTOBUF_TYPE.equals(actualAccept)) { return new ProduceProtoBufferProcessor(operationMeta, operationMeta.getSchemaMeta().getSwagger(), response.getContent().get(actualAccept).getSchema()); } if (MediaType.SERVER_SENT_EVENTS.equals(actualAccept)) { return new ProduceEventStreamProcessor(); } // text plain return findPlainProcessorByViewClass(serialViewClass); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.swagger.generator.SwaggerConst; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; public class ProduceProtoBufferProcessor implements ProduceProcessor { public static final String RESPONSE_MESSAGE_NAME = "X_RESPONSE"; public static final String EXT_ID = "protobuf"; private static final Object LOCK = new Object(); private final OperationMeta operationMeta; private final OpenAPI openAPI; private final Schema schema; private final ScopedProtobufSchemaManager scopedProtobufSchemaManager; public ProduceProtoBufferProcessor(OperationMeta operationMeta, OpenAPI openAPI, Schema schema) { this.operationMeta = operationMeta; this.openAPI = openAPI; this.schema = schema; this.scopedProtobufSchemaManager = getOrCreateScopedProtobufSchemaManager(operationMeta.getMicroserviceMeta()); } private ScopedProtobufSchemaManager getOrCreateScopedProtobufSchemaManager(MicroserviceMeta microserviceMeta) { ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); if (scopedProtobufSchemaManager == null) { synchronized (LOCK) { scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); if (scopedProtobufSchemaManager == null) { scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); } } } return scopedProtobufSchemaManager; } @Override public String getName() { return SwaggerConst.PROTOBUF_TYPE; } @Override public int getOrder() { return 0; } @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { ProtoMapper protoMapper = scopedProtobufSchemaManager .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), RESPONSE_MESSAGE_NAME, schema); RootSerializer serializer = protoMapper.getSerializerSchemaManager() .createRootSerializer(protoMapper.getProto().getMessage(RESPONSE_MESSAGE_NAME), Object.class); Map bodyArg = new HashMap<>(1); bodyArg.put("value", result); output.write(serializer.serialize(bodyArg)); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { ProtoMapper protoMapper = scopedProtobufSchemaManager .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), RESPONSE_MESSAGE_NAME, schema); RootDeserializer> deserializer = protoMapper.getDeserializerSchemaManager() .createRootDeserializer(protoMapper.getProto().getMessage(RESPONSE_MESSAGE_NAME), type); PropertyWrapper result = deserializer.deserialize(input.readAllBytes()); return result.getValue(); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import com.fasterxml.jackson.databind.JavaType; import jakarta.ws.rs.core.MediaType; public class ProduceTextPlainProcessor extends ProduceJsonProcessor { @Override public String getName() { return MediaType.TEXT_PLAIN; } @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { if (result instanceof String) { output.write(((String) result).getBytes(StandardCharsets.UTF_8)); return; } super.doEncodeResponse(output, result); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { if (String.class.equals(type.getRawClass())) { return IOUtils.toString(input, StandardCharsets.UTF_8); } return super.doDecodeResponse(input, type); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; public abstract class AbstractQueryCodec implements QueryCodec { private final String codecName; public AbstractQueryCodec(String codecName) { this.codecName = codecName; } @Override public String getCodecName() { return codecName; } @Override @SuppressWarnings("unchecked") public void encode(URLPathStringBuilder builder, String name, Object value) throws Exception { if (value == null) { // not write query key to express "null" return; } if (value.getClass().isArray()) { if (!(value instanceof Object[])) { value = RestObjectMapperFactory.getRestObjectMapper() .convertValue(value, Object[].class); } encode(builder, name, Arrays.asList((Object[]) value)); return; } if (value instanceof Collection) { encode(builder, name, (Collection) value); return; } encode(builder, name, Collections.singletonList(value)); } abstract void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception; } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.springframework.core.Ordered; import jakarta.servlet.http.HttpServletRequest; /** * bigger order will override the same name codec */ public interface QueryCodec extends Ordered { static String encodeValue(Object value) throws UnsupportedEncodingException { return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name()); } // can not be replaced by value.toString() because of date serialize static String convertToString(Object value) throws Exception { return RestObjectMapperFactory.getRestObjectMapper().convertToString(value); } @Override default int getOrder() { return 0; } String getCodecName(); void encode(URLPathStringBuilder builder, String name, Object value) throws Exception; Object decode(QueryProcessor processor, HttpServletRequest request); } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsv.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; /** * ?query=x1,x2 */ public class QueryCodecCsv extends QueryCodecWithDelimiter { public static final String CODEC_NAME = "form:0"; public static final String DELIMITER = ","; public QueryCodecCsv() { super(CODEC_NAME, DELIMITER, DELIMITER); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Collection; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import jakarta.servlet.http.HttpServletRequest; @SuppressWarnings("unchecked") public class QueryCodecMulti extends AbstractQueryCodec { public static final String CODEC_NAME = "form:1"; public QueryCodecMulti() { super(CODEC_NAME); } @Override public void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception { for (Object value : values) { if (value == null) { continue; } String strValue = QueryCodec.convertToString(value); builder.appendQuery(name, QueryCodec.encodeValue(strValue)); } } @Override public Object decode(QueryProcessor processor, HttpServletRequest request) { if (processor.isRepeatedType()) { //Even if the paramPath does not exist, value won't be null at now String[] values = request.getParameterValues(processor.getParameterPath()); // compatible to SpringMVC @RequestParam. BODY_PARAMETER is only set for SpringMVC. if (values == null || values.length == 0) { Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); if (forms == null) { return processor.convertValue(values); } Object formValue = forms.get(processor.getParameterPath()); if (formValue == null) { return processor.convertValue(values); } if (formValue instanceof String[]) { values = (String[]) formValue; } else { values = new String[] {formValue.toString()}; } } return processor.convertValue(values); } Object value = processor.getAndCheckParameter(request); return processor.convertValue(value); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; /** * ?query=x1|x2 */ public class QueryCodecPipes extends QueryCodecWithDelimiter { public static final String CODEC_NAME = "pipeDelimited:0"; public static final String JOIN_DELIMITER = "|"; public static final String SPLIT_DELIMITER = "\\|"; public QueryCodecPipes() { super(CODEC_NAME, JOIN_DELIMITER, SPLIT_DELIMITER); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsv.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; /** * ?query=x1%20x2 */ public class QueryCodecSsv extends QueryCodecWithDelimiter { public static final String CODEC_NAME = "spaceDelimited:0"; public static final String DELIMITER = " "; public QueryCodecSsv() { super(CODEC_NAME, DELIMITER, DELIMITER); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecWithDelimiter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Collection; import java.util.StringJoiner; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import jakarta.servlet.http.HttpServletRequest; /** * can not support value with delimiter
* a csv example, a collection with two values:
* 1. a
* 2. b,c
* will encode to be a%2Cb%2Cc, this is ambiguous */ public class QueryCodecWithDelimiter extends AbstractQueryCodec { private final String joinDelimiter; private final String splitDelimiter; public QueryCodecWithDelimiter(String codecName, String joinDelimiter, String splitDelimiter) { super(codecName); this.joinDelimiter = joinDelimiter; this.splitDelimiter = splitDelimiter; } @Override public void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception { String joined = join(values); if (joined == null) { return; } builder.appendQuery(name, joined); } /** *
   *   SwaggerIde:
   *   1. encode query value by uri rule, not url rule
   *      part of the difference:
   *               uri  url
   *        space  %20  +
   *        [      [    %5B
   *      some difference will cause tomcat parse url failed
   *      so we encode query value by url rule
   *   2. encode each element
   *        for pipes, SwaggerIde will encode [a, b] to a|b
   *        but this will cause problem when run with tomcat
   *        so we encode the joined value, not encode each element
   * 
* @param values values to be joined * @return joined value */ protected String join(Collection values) throws Exception { StringJoiner joiner = new StringJoiner(joinDelimiter); boolean hasValue = false; for (Object value : values) { if (value != null) { String strValue = QueryCodec.convertToString(value); joiner.add(strValue); hasValue = true; } } return hasValue ? QueryCodec.encodeValue(joiner.toString()) : null; } @Override public Object decode(QueryProcessor processor, HttpServletRequest request) { Object value = processor.getAndCheckParameter(request); value = value != null ? value.toString().split(splitDelimiter, -1) : new String[0]; return processor.convertValue(value); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QueryCodecs { private static final Logger LOGGER = LoggerFactory.getLogger(QueryCodecs.class); static QueryCodecs createForTest() { return new QueryCodecs(Arrays.asList( new QueryCodecMulti(), new QueryCodecCsv(), new QueryCodecSsv(), new QueryCodecPipes() )); } private final Map codecs = new HashMap<>(); private final QueryCodec defaultCodec; public QueryCodecs(List orderedCodecs) { orderedCodecs.forEach(this::register); defaultCodec = codecs.get(QueryCodecMulti.CODEC_NAME); } private void register(QueryCodec codec) { QueryCodec exists = codecs.put(codec.getCodecName(), codec); if (exists != null) { LOGGER.info("override QueryCodec, exists={}, new={}.", exists.getClass().getName(), codec.getClass().getName()); } } public QueryCodec find(String name) { if (name == null) { return defaultCodec; } QueryCodec codec = codecs.get(name); if (codec == null) { throw new IllegalStateException("not support QueryCodec, name=" + name); } return codec; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; public class QueryCodecsUtils { // create a default instance, so that more friendly to UT private static QueryCodecs queryCodecs = QueryCodecs.createForTest(); public QueryCodecsUtils(QueryCodecs queryCodecs) { QueryCodecsUtils.queryCodecs = queryCodecs; } public static QueryCodec find(Parameter.StyleEnum styleEnum, Boolean explode) { return queryCodecs.find(formatName(styleEnum, explode)); } private static String formatName(StyleEnum styleEnum, Boolean explode) { if (styleEnum == null) { return null; } return styleEnum + ":" + (explode != null && explode ? "1" : "0"); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestMetaUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.definition.OperationMeta; public final class RestMetaUtils { private RestMetaUtils() { } public static RestOperationMeta getRestOperationMeta(OperationMeta operationMeta) { return operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import java.io.Serializable; import java.util.Comparator; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; /** * 用于RestOperation的排序 */ public class RestOperationComparator implements Serializable, Comparator { private static final long serialVersionUID = -2364909265520813678L; @Override public int compare(RestOperationMeta r1, RestOperationMeta r2) { // 排序规则: // 1.静态字符多的优先 // 2.变量组多的优先 // 3.带正则表达式的优先 // 如: // /customers/{id}/{name}/address // /customers/{id : .+}/address // /customers/{id}/address // /customers/{id : .+} PathRegExp path1 = r1.getAbsolutePathRegExp(); PathRegExp path2 = r2.getAbsolutePathRegExp(); int staticCompare = path2.getStaticCharCount() - path1.getStaticCharCount(); if (staticCompare != 0) { return staticCompare; } int groupCompare = path2.getGroupCount() - path1.getGroupCount(); if (groupCompare != 0) { return groupCompare; } return path2.getGroupWithRegExpCount() - path1.getGroupWithRegExpCount(); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.PartProcessor; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; @SuppressWarnings("rawtypes") public class RestOperationMeta { private static final Logger LOGGER = LoggerFactory.getLogger(RestOperationMeta.class); protected OperationMeta operationMeta; protected boolean formData; // make sure if response is file protected boolean downloadFile; protected boolean serverSendEvents; protected List paramList = new ArrayList<>(); // key为参数名 protected Map paramMap = new LinkedHashMap<>(); protected List fileKeys = new ArrayList<>(); protected String absolutePath; protected PathRegExp absolutePathRegExp; // 快速构建URL path private URLPathBuilder pathBuilder; public void init(OperationMeta operationMeta) { this.operationMeta = operationMeta; OpenAPI swagger = operationMeta.getSchemaMeta().getSwagger(); Operation operation = operationMeta.getSwaggerOperation(); this.downloadFile = checkDownloadFileFlag(); this.serverSendEvents = checkServerSendEvents(); if (operation.getParameters() != null) { for (int swaggerParameterIdx = 0; swaggerParameterIdx < operation.getParameters().size(); swaggerParameterIdx++) { Parameter parameter = operation.getParameters().get(swaggerParameterIdx); Type type = operationMeta.getSwaggerProducerOperation() != null ? operationMeta.getSwaggerProducerOperation() .getSwaggerParameterTypes().get(parameter.getName()) : null; RestParam param = new RestParam(operationMeta, parameter, type); addParam(param); } } if (operation.getRequestBody() != null) { if (isFormParameters(operation)) { formData = true; Schema formSchema = formSchemas(operation); if (formSchema != null) { formSchema.getProperties().forEach((k, v) -> { addRestParamByName(operationMeta, (String) k, operation); }); } } else { addRestParamByName(operationMeta, (String) operation.getRequestBody().getExtensions().get(SwaggerConst.EXT_BODY_NAME), operation); } } setAbsolutePath(SwaggerUtils.concatAbsolutePath(swagger, operationMeta.getOperationPath())); } private void addRestParamByName(OperationMeta operationMeta, String name, Operation operation) { Type type = operationMeta.getSwaggerProducerOperation() != null ? operationMeta.getSwaggerProducerOperation() .getSwaggerParameterTypes().get(name) : null; RestParam param = new RestParam(operationMeta, name, operation.getRequestBody(), formData, type); addParam(param); } private boolean isFormParameters(Operation operation) { return operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null || operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null; } private Schema formSchemas(Operation operation) { if (operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { return operation.getRequestBody().getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema(); } return operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema(); } public boolean isDownloadFile() { return downloadFile; } public boolean isServerSendEvents() { return serverSendEvents; } private boolean checkServerSendEvents() { ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); if (responses == null) { return false; } ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); return response != null && response.getContent() != null && response.getContent().get(MediaType.SERVER_SENT_EVENTS) != null; } private boolean checkDownloadFileFlag() { ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); if (responses == null) { return false; } ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); if (response != null && response.getContent() != null) { for (io.swagger.v3.oas.models.media.MediaType mediaType : response.getContent().values()) { if (mediaType.getSchema() != null && "string".equals(mediaType.getSchema().getType()) && "binary".equals(mediaType.getSchema().getFormat())) { return true; } } } return false; } public boolean isFormData() { return formData; } public void setOperationMeta(OperationMeta operationMeta) { this.operationMeta = operationMeta; } public String getAbsolutePath() { return this.absolutePath; } public void setAbsolutePath(String absolutePath) { this.absolutePath = absolutePath; this.absolutePathRegExp = createPathRegExp(absolutePath); this.pathBuilder = new URLPathBuilder(absolutePath, paramMap); } public PathRegExp getAbsolutePathRegExp() { return this.absolutePathRegExp; } public boolean isAbsoluteStaticPath() { return this.absolutePathRegExp.isStaticPath(); } protected PathRegExp createPathRegExp(String path) { if (path == null || path.equals("")) { throw new Error("null rest url is not supported"); } try { return new PathRegExp(OperationLocator.getStandardPath(path)); } catch (Exception e) { LOGGER.error(e.getMessage()); return null; } } public RestParam getParamByName(String name) { return paramMap.get(name); } public OperationMeta getOperationMeta() { return operationMeta; } public URLPathBuilder getPathBuilder() { return this.pathBuilder; } public List getParamList() { return paramList; } private void addParam(RestParam param) { if (param.getParamProcessor() instanceof PartProcessor) { fileKeys.add(param.getParamName()); } paramList.add(param); paramMap.put(param.getParamName(), param); } public String getHttpMethod() { return operationMeta.getHttpMethod(); } public List getFileKeys() { return fileKeys; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import java.lang.reflect.Type; import java.util.Collection; import java.util.Map; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessor; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessorCreator; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessorCreatorManager; import org.apache.servicecomb.core.definition.OperationMeta; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; @SuppressWarnings({"rawtypes", "unchecked"}) public class RestParam { private static final JavaType STRING_ARRAY_TYPE = TypeFactory.defaultInstance().constructArrayType(String.class); protected ParamValueProcessor paramProcessor; protected String paramName; public RestParam(OperationMeta operationMeta, Parameter parameter, Type genericParamType) { this.paramName = parameter.getName(); init(operationMeta, parameter, genericParamType); } public RestParam(OperationMeta operationMeta, String paramName, RequestBody parameter, boolean isForm, Type genericParamType) { this.paramName = paramName; init(operationMeta, parameter, isForm, genericParamType); } public ParamValueProcessor getParamProcessor() { return this.paramProcessor; } public void setParamProcessor(ParamValueProcessor paramProcessor) { this.paramProcessor = paramProcessor; } public String getParamName() { return paramName; } protected void init(OperationMeta operationMeta, Parameter parameter, Type genericParamType) { String paramType = parameter.getIn(); ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(paramType); this.setParamProcessor(creator.create(operationMeta, parameter.getName(), parameter, genericParamType)); } protected void init(OperationMeta operationMeta, RequestBody parameter, boolean isForm, Type genericParamType) { ParamValueProcessorCreator creator; if (isForm) { creator = ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(FormProcessorCreator.PARAMTYPE); } else { creator = ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(BodyProcessorCreator.PARAM_TYPE); } this.setParamProcessor(creator.create(operationMeta, this.paramName, parameter, genericParamType)); } public T getValue(Map args) { return (T) args.get(paramName); } public String[] getValueAsStrings(Map args) { Object value = args.get(paramName); if (value == null) { return null; } if (value.getClass().isArray() || value instanceof Collection) { return (String[]) paramProcessor.convertValue(value, STRING_ARRAY_TYPE); } return new String[] {String.valueOf(value)}; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.Map; import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.common.rest.definition.RestParam; public abstract class AbstractUrlParamWriter implements UrlParamWriter { protected RestParam param; @VisibleForTesting public Object getParamValue(Map args) { if (param == null) { // Wrong server definition // @GetMapping(path = "/getLocalDateTime/{paramX}") // public LocalDateTime getLocalDateTimePath(@PathParam("paramY") LocalDateTime date) { throw new IllegalArgumentException("Path parameter name not valid in provider. Check if provider " + "path pattern has the parameter name."); } return args.get(param.getParamName()); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathRegExp.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 处理path中的正则表达式 */ public class PathRegExp { // 不带正则表达式的group,使用这个作为正则表达式 private static final String DEFAULT_REG_EXP = "[^/]+?"; private static final byte NAME_READ = 2; private static final byte NAME_READ_READY = 3; private static final byte NAME_READ_START = 1; private static final byte REGEXP_READ = 12; private static final byte REGEXP_READ_READY = 13; private static final byte REGEXP_READ_START = 11; public static final String SLASH = "/"; // 静态字符的个数 protected int staticCharCount; // 包括带正则表达式和不带正则表达式的group,总数 protected int groupCount; // 带正则表达式的group数 protected int groupWithRegExpCount = 0; protected final Pattern pattern; protected final List varNames = new ArrayList<>(); public static String ensureEndWithSlash(String path) { if (path.endsWith(SLASH)) { return path; } return path + "/"; } // 调用者已经保证path不以/打头 public PathRegExp(String path) throws Exception { // a/{id}/{name:.+}/{age}/c变成下面的表达式 // a/([^/]+?)/(.+)/([^/]+?)/c/(.*) final int pathLength = path.length(); final StringBuilder pathPattern = new StringBuilder(); for (int i = 0; i < pathLength; i++) { final char c = path.charAt(i); switch (c) { case '{': i = processGroup(path, i, pathPattern); groupCount++; break; case '}': throw new Exception("'}' is only allowed as " + "end of a variable name in \"" + path + "\""); case ';': throw new Exception("matrix parameters are not allowed in \"" + path + "\""); default: pathPattern.append(c); staticCharCount++; } } if (pathPattern.length() > 0 && pathPattern.charAt(pathPattern.length() - 1) != '/') { pathPattern.append('/'); } pathPattern.append("(.*)"); pattern = Pattern.compile(pathPattern.toString()); } protected int processGroup(final String path, final int braceIndex, final StringBuilder pathPattern) throws Exception { pathPattern.append('('); final int pathLength = path.length(); final StringBuilder varName = new StringBuilder(); final StringBuilder regExp = new StringBuilder(); int state = NAME_READ_START; for (int i = braceIndex + 1; i < pathLength; i++) { final char c = path.charAt(i); switch (c) { case '{': throw new Exception("A variable must not contain an extra '{' in \"" + path + "\""); case ' ': case '\t': state = processLineBreak(state); break; case ':': state = processColon(path, braceIndex, state); break; case '}': processBrace(path, pathPattern, varName, regExp, state); return i; default: state = processDefault(path, varName, regExp, state, i, c); break; } } throw new Exception("No '}' found after '{' " + "at position " + braceIndex + " of \"" + path + "\""); } private int processDefault(final String path, final StringBuilder varName, final StringBuilder regExp, int state, int i, final char c) throws Exception { if (state == NAME_READ_START) { state = NAME_READ; varName.append(c); } else if (state == NAME_READ) { varName.append(c); } else if (state == REGEXP_READ_START) { state = REGEXP_READ; regExp.append(c); } else if (state == REGEXP_READ) { regExp.append(c); } else { throw new Exception("Invalid character found at position " + i + " of \"" + path + "\""); } return state; } private void processBrace(final String path, final StringBuilder pathPattern, final StringBuilder varName, final StringBuilder regExp, int state) throws Exception { if (state == NAME_READ_START) { throw new Exception( "The template variable name '{}' is not allowed in " + "\"" + path + "\""); } if ((state == REGEXP_READ) || (state == REGEXP_READ_READY)) { pathPattern.append(regExp); if (!regExp.toString().equals(DEFAULT_REG_EXP)) { groupWithRegExpCount++; } } else { pathPattern.append(DEFAULT_REG_EXP); } pathPattern.append(')'); this.varNames.add(varName.toString()); } private int processColon(final String path, final int braceIndex, int state) throws Exception { if (state == NAME_READ_START) { throw new Exception( "The variable name at position must not be null at " + braceIndex + " of \"" + path + "\""); } if (state == NAME_READ || state == NAME_READ_READY) { state = REGEXP_READ_START; } return state; } private int processLineBreak(int state) { if (state == NAME_READ) { state = NAME_READ_READY; } else if (state == REGEXP_READ) { state = REGEXP_READ_READY; } return state; } // 已知/customers/{id}/address/{id} // @PathParam("id") String addressId // url:/customers/123/address/456 // 则addressId取值为456 // 即后面的总是覆盖前面的 public String match(String path, Map varValues) { Matcher matcher = pattern.matcher(path); if (!matcher.matches()) { return null; } for (int i = 1; i < matcher.groupCount(); i++) { varValues.put(varNames.get(i - 1), matcher.group(i)); } return matcher.group(matcher.groupCount()); } @Override public String toString() { return this.pattern.pattern(); } public boolean isStaticPath() { return groupCount == 0; } public int getStaticCharCount() { return staticCharCount; } public int getGroupCount() { return groupCount; } public int getGroupWithRegExpCount() { return groupWithRegExpCount; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.Map; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.apache.servicecomb.foundation.common.http.HttpUtils; /** * Dynamically processing path */ public class PathVarParamWriter extends AbstractUrlParamWriter { public PathVarParamWriter(RestParam param) { this.param = param; } @Override public void write(URLPathStringBuilder builder, Map args) throws Exception { if (getParamValue(args) == null) { throw new IllegalArgumentException("path parameter can not be null."); } String encodedPathParam = encodeNotNullValue(getParamValue(args)); builder.appendPath(encodedPathParam); } private String encodeNotNullValue(Object value) throws Exception { String strValue = RestObjectMapperFactory.getRestObjectMapper().convertToString(value); return HttpUtils.encodePathParam(strValue); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.Map; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.codec.query.QueryCodec; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; public class QueryVarParamWriter extends AbstractUrlParamWriter { private final QueryCodec queryCodec; public QueryVarParamWriter(RestParam param) { this.param = param; this.queryCodec = ((QueryProcessor) param.getParamProcessor()).getQueryCodec(); } @Override public void write(URLPathStringBuilder builder, Map args) throws Exception { Object value = getParamValue(args); queryCodec.encode(builder, param.getParamName(), value); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.Map; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; public class StaticUrlParamWriter implements UrlParamWriter { private final String staticPath; public StaticUrlParamWriter(String staticPath) { this.staticPath = staticPath; } @Override public void write(URLPathStringBuilder builder, Map args) { builder.appendPath(staticPath); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator; import org.apache.servicecomb.common.rest.definition.RestParam; /** * 初始化阶段创建URLPathBuilder,用于加速调用阶段的path创建 */ public class URLPathBuilder { private final List pathParamWriterList = new ArrayList<>(); private final List queryParamWriterList = new ArrayList<>(); private static final String SLASH = "/"; public URLPathBuilder(String rawPath, Map paramMap) { initPathWriterList(rawPath, paramMap); initQueryWriterList(paramMap); } private void initQueryWriterList(Map paramMap) { for (RestParam param : paramMap.values()) { if (!QueryProcessorCreator.PARAMTYPE.equals(param.getParamProcessor().getProcessorType())) { continue; } UrlParamWriter dynamicWriter = new QueryVarParamWriter(param); queryParamWriterList.add(dynamicWriter); } } private void initPathWriterList(String rawPath, Map paramMap) { // 首部加上'/' if (!rawPath.startsWith(SLASH)) { rawPath = SLASH + rawPath; } StringBuilder tmpPath = new StringBuilder(); for (int idx = 0; idx < rawPath.length(); idx++) { char currentChar = rawPath.charAt(idx); if (currentChar == '{') { if (tmpPath.length() != 0) { this.pathParamWriterList.add(new StaticUrlParamWriter(tmpPath.toString())); tmpPath.setLength(0); } } else if (currentChar == '}') { if (tmpPath.length() == 0) { continue; } String tmpPathStr = tmpPath.toString(); String pathParamName = tmpPathStr; if (tmpPathStr.contains(":")) { pathParamName = tmpPathStr.split(":", 2)[0].trim(); } RestParam param = paramMap.get(pathParamName); this.pathParamWriterList.add(new PathVarParamWriter(param)); tmpPath.setLength(0); } else { tmpPath.append(currentChar); } } if (tmpPath.length() != 0) { this.pathParamWriterList.add(new StaticUrlParamWriter(tmpPath.toString())); } } public String createRequestPath(Map args) throws Exception { URLPathStringBuilder builder = new URLPathStringBuilder(); genPathString(builder, args); genQueryString(builder, args); return builder.build(); } public String createPathString(Map args) throws Exception { URLPathStringBuilder builder = new URLPathStringBuilder(); genPathString(builder, args); return builder.build(); } private void genPathString(URLPathStringBuilder builder, Map args) throws Exception { for (UrlParamWriter writer : this.pathParamWriterList) { writer.write(builder, args); } } private void genQueryString(URLPathStringBuilder builder, Map args) throws Exception { for (UrlParamWriter writer : queryParamWriterList) { writer.write(builder, args); } } public static class URLPathStringBuilder { private final StringBuilder stringBuilder = new StringBuilder(); private boolean queryPrefixNotWrite = true; public URLPathStringBuilder appendPath(String s) { stringBuilder.append(s); return this; } public URLPathStringBuilder appendQuery(String name, String encodedValue) { if (queryPrefixNotWrite) { stringBuilder.append('?'); queryPrefixNotWrite = false; } else { stringBuilder.append('&'); } stringBuilder.append(name).append("=").append(encodedValue); return this; } public String build() { return stringBuilder.toString(); } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.Map; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; public interface UrlParamWriter { void write(URLPathStringBuilder builder, Map args) throws Exception; } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.filter.inner; import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestCodec; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.PartUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.TransportContext; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; public class RestServerCodecFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(RestServerCodecFilter.class); public static final String NAME = "rest-server-codec"; @Override public String getName() { return NAME; } @Override public int getOrder() { // almost time, should be the first filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; } @Override public boolean enabledForTransport(String transport) { return CoreConst.RESTFUL.equals(transport); } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return CompletableFuture.completedFuture(invocation) .thenAccept(this::decodeRequest) .thenCompose(v -> invokeNext(invocation, nextNode)) .exceptionally(exception -> toProducerResponse(invocation, exception)) .thenCompose(response -> encodeResponse(invocation, response)); } protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { if (invocation.isEdge()) { TransportContext transportContext = invocation.getTransportContext(); return nextNode.onFilter(invocation).whenComplete((r, e) -> invocation.setTransportContext(transportContext)); } return nextNode.onFilter(invocation); } protected void decodeRequest(Invocation invocation) { invocation.getInvocationStageTrace().startProviderDecodeRequest(); HttpServletRequestEx requestEx = invocation.getRequestEx(); OperationMeta operationMeta = invocation.getOperationMeta(); RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); Map swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta); invocation.setSwaggerArguments(swaggerArguments); invocation.getInvocationStageTrace().finishProviderDecodeRequest(); } protected CompletableFuture encodeResponse(Invocation invocation, Response response) { invocation.onEncodeResponseStart(response); HttpTransportContext transportContext = invocation.getTransportContext(); HttpServletResponseEx responseEx = transportContext.getResponseEx(); // TODO: response support JsonView ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE .createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), invocation.getRequestEx().getHeader(HttpHeaders.ACCEPT), null); return encodeResponse(invocation, response, produceProcessor, responseEx) .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); } private static boolean isFailedResponse(Response response) { return response.getResult() instanceof InvocationException; } private static CompletableFuture writePart( HttpServletResponseEx responseEx, Object data, Response response) { CompletableFuture result = new CompletableFuture<>(); responseEx.sendPart(PartUtils.getSinglePart(null, data)) .whenComplete((r, e) -> { if (e != null) { result.completeExceptionally(e); return; } result.complete(response); }); return result; } private static CompletableFuture writeResponse( HttpServletResponseEx responseEx, ProduceProcessor produceProcessor, Object data, Response response, boolean commit) { try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) { produceProcessor.encodeResponse(output, data); CompletableFuture result = new CompletableFuture<>(); responseEx.setBodyBuffer(output.getBuffer()); // For extensions usage if (commit) { responseEx.setContentLength(output.getBuffer().length()); } responseEx.sendBuffer(output.getBuffer()).whenComplete((v, e) -> { if (e != null) { result.completeExceptionally(e); return; } if (!commit) { try { responseEx.flushBuffer(); } catch (IOException ex) { LOGGER.warn("Failed to flush buffer for Server Send Events", ex); } } result.complete(response); }); return result; } catch (Throwable e) { LOGGER.error("internal service error must be fixed.", e); responseEx.setStatus(500); return CompletableFuture.failedFuture(e); } } public static CompletableFuture encodeResponse(Invocation invocation, Response response, ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) { responseEx.setStatus(response.getStatusCode()); copyHeadersToHttpResponse(invocation, response.getHeaders(), responseEx); if (isFailedResponse(response)) { responseEx.setContentType(produceProcessor.getName()); return writeResponse(responseEx, produceProcessor, ((InvocationException) response.getResult()).getErrorData(), response, true); } if (isDownloadFileResponseType(invocation, response)) { return writePart(responseEx, response.getResult(), response); } if (isServerSendEvent(response)) { responseEx.setContentType(produceProcessor.getName()); return writeServerSendEvent(response, produceProcessor, responseEx); } responseEx.setContentType(produceProcessor.getName()); return writeResponse(responseEx, produceProcessor, response.getResult(), response, true); } private static CompletableFuture writeServerSendEvent(Response response, ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) { responseEx.setChunked(true); CompletableFuture result = new CompletableFuture<>(); Publisher publisher = response.getResult(); publisher.subscribe(new Subscriber() { Subscription subscription; @Override public void onSubscribe(Subscription s) { s.request(1); subscription = s; } @Override public void onNext(Object o) { writeResponse(responseEx, produceProcessor, o, response, false).whenComplete((r, e) -> { if (e != null) { subscription.cancel(); result.completeExceptionally(e); return; } subscription.request(1); }); } @Override public void onError(Throwable t) { result.completeExceptionally(t); } @Override public void onComplete() { result.complete(response); } }); return result; } /** * Check whether this response is a downloaded file response, * according to the schema recorded in {@link org.apache.servicecomb.swagger.invocation.response.ResponsesMeta} * and response status code. * @return true if this response is a downloaded file, otherwise false. */ public static boolean isDownloadFileResponseType(Invocation invocation, Response response) { return Part.class.isAssignableFrom( invocation.findResponseType(response.getStatusCode()).getRawClass()); } public static boolean isServerSendEvent(Response response) { return response.getResult() instanceof Publisher; } public static void copyHeadersToHttpResponse(Invocation invocation, MultiMap headers, HttpServletResponseEx responseEx) { if (headers != null) { headers.remove(CONTENT_LENGTH); headers.remove(TRANSFER_ENCODING); for (Entry entry : headers.entries()) { responseEx.addHeader(entry.getKey(), entry.getValue()); } } if (invocation != null && responseEx.getHeader(CoreConst.TRACE_ID_NAME) == null) { responseEx.addHeader(CoreConst.TRACE_ID_NAME, invocation.getTraceId()); } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.filter.inner; import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; import java.util.HashMap; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.common.rest.WebSocketTransportContext; import org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.TransportContext; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.ServerWebSocket; public class WebSocketServerCodecFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServerCodecFilter.class); public static final String NAME = "websocket-codec"; @Override public String getName() { return NAME; } @Override public int getOrder() { // almost time, should be the first filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; } @Override public boolean enabledForTransport(String transport) { return CoreConst.WEBSOCKET.equals(transport); } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return CompletableFuture.completedFuture(invocation) .thenCompose(this::decodeRequest) .thenCompose(v -> invokeNext(invocation, nextNode)) .exceptionally(exception -> toProducerResponse(invocation, exception)) .thenCompose(response -> encodeResponse(invocation, response)); } protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { if (invocation.isEdge()) { TransportContext transportContext = invocation.getTransportContext(); return nextNode.onFilter(invocation).whenComplete((r, e) -> invocation.setTransportContext(transportContext)); } return nextNode.onFilter(invocation); } protected CompletableFuture decodeRequest(Invocation invocation) { invocation.getInvocationStageTrace().startProviderDecodeRequest(); invocation.setSwaggerArguments(new HashMap<>()); // set context parameters and do nothing else. invocation.getInvocationStageTrace().finishProviderDecodeRequest(); return CompletableFuture.completedFuture(null); } protected CompletableFuture encodeResponse(Invocation invocation, Response response) { invocation.onEncodeResponseStart(response); WebSocketTransportContext context = invocation.getTransportContext(); return encodeResponse(response, context.getServerWebSocket()) .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); } private static boolean isFailedResponse(Response response) { return response.getResult() instanceof InvocationException; } private static CompletableFuture writeResponse( ServerWebSocket webSocket, Object data, Response response) { try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) { ProduceJsonProcessor produceProcessor = new ProduceJsonProcessor(); produceProcessor.encodeResponse(output, data); CompletableFuture result = new CompletableFuture<>(); webSocket.write(output.getBuffer()).onComplete(v -> result.complete(response), result::completeExceptionally); return result; } catch (Throwable e) { LOGGER.error("internal service error must be fixed.", e); return CompletableFuture.failedFuture(e); } } public static CompletableFuture encodeResponse(Response response, ServerWebSocket webSocket) { if (isFailedResponse(response)) { return writeResponse(webSocket, ((InvocationException) response.getResult()).getErrorData(), response); } return CompletableFuture.completedFuture(response); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/MicroservicePaths.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.common.rest.definition.RestOperationComparator; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MicroservicePaths { private static final Logger LOGGER = LoggerFactory.getLogger(MicroservicePaths.class); // 运行阶段,静态path,一次直接查找到目标,不必遍历查找 // 以path为key protected Map staticPathOperations = new HashMap<>(); // 运行阶段,以path优先级,从高到低排列的operation列表 protected List dynamicPathOperationsList = new ArrayList<>(); public void sortPath() { RestOperationComparator comparator = new RestOperationComparator(); this.dynamicPathOperationsList.sort(comparator); } public void addResource(RestOperationMeta swaggerRestOperation) { if (swaggerRestOperation.isAbsoluteStaticPath()) { // 静态path addStaticPathResource(swaggerRestOperation); return; } dynamicPathOperationsList.add(swaggerRestOperation); } protected void addStaticPathResource(RestOperationMeta operation) { String httpMethod = operation.getHttpMethod(); String path = OperationLocator.getStandardPath(operation.getAbsolutePath()); OperationGroup group = staticPathOperations.get(path); if (group == null) { group = new OperationGroup(); group.register(httpMethod, operation); staticPathOperations.put(path, group); return; } if (group.findValue(httpMethod) == null) { group.register(httpMethod, operation); return; } throw new ServiceCombException( String.format("operation with url %s, method %s is duplicated.", path, httpMethod)); } public Map getStaticPathOperationMap() { return staticPathOperations; } public List getDynamicPathOperationList() { return dynamicPathOperationsList; } public void printPaths() { for (Entry entry : staticPathOperations.entrySet()) { OperationGroup operationGroup = entry.getValue(); printPath(operationGroup.values()); } printPath(getDynamicPathOperationList()); } protected void printPath(Collection operations) { for (RestOperationMeta operation : operations) { SwaggerProducerOperation producerOperation = operation.getOperationMeta().getSwaggerProducerOperation(); LOGGER.debug("Swagger mapped \"{[{}], method=[{}]}\" onto {}", operation.getAbsolutePath(), operation.getHttpMethod(), producerOperation.getProducerMethod()); } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationGroup.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.foundation.common.RegisterManager; /** * 存放具有相同path,不同httpmethod的operation */ public class OperationGroup extends RegisterManager { private static final String NAME = "operation group manager"; public OperationGroup() { super(NAME); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jakarta.ws.rs.core.Response.Status; /** * 从path和http method定位到具体的operation */ public class OperationLocator { private static final Logger LOGGER = LoggerFactory.getLogger(OperationLocator.class); private static final String SLASH = "/"; protected RestOperationMeta operation; protected Map pathVarMap = new HashMap<>(); protected boolean resourceFound = false; public RestOperationMeta getOperation() { return this.operation; } public Map getPathVarMap() { return this.pathVarMap; } // 先在静态路径operation list中查找;如果找不到,则在动态路径operation list中查找 public void locate(String microserviceName, String path, String httpMethod, MicroservicePaths microservicePaths) { // 在静态路径中查找 operation = locateStaticPathOperation(path, httpMethod, microservicePaths.getStaticPathOperationMap()); if (operation != null) { // 全部定位完成 return; } // 在动态路径中查找 operation = locateDynamicPathOperation(path, microservicePaths.getDynamicPathOperationList(), httpMethod); if (operation != null) { return; } Status status = Status.NOT_FOUND; if (resourceFound) { status = Status.METHOD_NOT_ALLOWED; } LOGGER.error("locate path failed, status:{}, http method:{}, path:{}, microserviceName:{}", status, httpMethod, path, microserviceName); throw new InvocationException(status, status.getReasonPhrase()); } protected RestOperationMeta locateStaticPathOperation(String path, String httpMethod, Map staticPathOperations) { OperationGroup group = staticPathOperations.get(path); if (group == null) { return null; } resourceFound = true; return group.findValue(httpMethod); } protected RestOperationMeta locateDynamicPathOperation(String path, Collection resourceList, String httpMethod) { for (RestOperationMeta resource : resourceList) { String remainPath = resource.getAbsolutePathRegExp().match(path, pathVarMap); // 刚好匹配,不多也不少 if ("".equals(remainPath)) { resourceFound = true; if (checkHttpMethod(resource, httpMethod)) { return resource; } } } return null; } protected boolean checkHttpMethod(RestOperationMeta operation, String httpMethod) { return operation.getHttpMethod().equals(httpMethod); } // Make path standard in order to build path mapping and find path operation. // NOTE: Path: /a/b/c -> /a/b/c/. Almost change path every time, this make performance lower. public static String getStandardPath(String path) { if (path.length() > 0 && !path.endsWith(SLASH)) { path += SLASH; } return path; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/ServicePathManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; /** * 对静态路径和动态路径的operation进行预先处理,加速operation的查询定位 */ public class ServicePathManager { private static final String REST_PATH_MANAGER = "RestServicePathManager"; protected MicroserviceMeta microserviceMeta; // equal to swagger protected MicroservicePaths swaggerPaths = new MicroservicePaths(); // we support swagger basePath is not include contextPath and urlPattern // so for producer, we must concat contextPath and urlPattern // only valid for microservice of this process protected MicroservicePaths producerPaths; public static ServicePathManager getServicePathManager(MicroserviceMeta microserviceMeta) { return microserviceMeta.getExtData(REST_PATH_MANAGER); } public ServicePathManager(MicroserviceMeta microserviceMeta) { this.microserviceMeta = microserviceMeta; for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas().values()) { addSchema(schemaMeta); } sortPath(); microserviceMeta.putExtData(REST_PATH_MANAGER, this); } private void addSchema(SchemaMeta schemaMeta) { for (OperationMeta operationMeta : schemaMeta.getOperations().values()) { RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(operationMeta); operationMeta.putExtData(RestConst.SWAGGER_REST_OPERATION, restOperationMeta); addResource(restOperationMeta); } } public OperationLocator consumerLocateOperation(String path, String httpMethod) { String standPath = OperationLocator.getStandardPath(path); OperationLocator locator = new OperationLocator(); locator.locate(microserviceMeta.getMicroserviceName(), standPath, httpMethod, swaggerPaths); return locator; } public OperationLocator producerLocateOperation(String path, String httpMethod) { String standPath = OperationLocator.getStandardPath(path); OperationLocator locator = new OperationLocator(); locator.locate(microserviceMeta.getMicroserviceName(), standPath, httpMethod, producerPaths); return locator; } public void addResource(RestOperationMeta swaggerRestOperation) { swaggerPaths.addResource(swaggerRestOperation); } public void sortPath() { swaggerPaths.sortPath(); } public void buildProducerPaths() { String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); if (StringUtils.isEmpty(urlPrefix)) { producerPaths = swaggerPaths; producerPaths.printPaths(); return; } producerPaths = new MicroservicePaths(); for (OperationGroup operationGroup : swaggerPaths.getStaticPathOperationMap().values()) { addProducerPaths(urlPrefix, operationGroup.values()); } addProducerPaths(urlPrefix, swaggerPaths.getDynamicPathOperationList()); producerPaths.printPaths(); } private void addProducerPaths(String urlPrefix, Collection restOperationMetas) { for (RestOperationMeta swaggerRestOperation : restOperationMetas) { RestOperationMeta producerRestOperation = swaggerRestOperation; if (!swaggerRestOperation.getAbsolutePath().startsWith(urlPrefix)) { producerRestOperation = new RestOperationMeta(); producerRestOperation.init(swaggerRestOperation.getOperationMeta()); producerRestOperation.setAbsolutePath(urlPrefix + swaggerRestOperation.getAbsolutePath()); } producerPaths.addResource(producerRestOperation); } } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/ClassPathStaticResourceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.resource; import java.io.IOException; import java.net.URL; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.InputStreamPart; public class ClassPathStaticResourceHandler extends StaticResourceHandler { protected Part findResource(String path) throws IOException { URL url = this.getClass().getClassLoader().getResource(path); if (url == null) { return null; } return new InputStreamPart(null, url.openStream()).setSubmittedFileName(path); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/StaticResourceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.resource; import java.io.IOException; import java.net.URI; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // TODO: LRU cache for small resource in jar? public abstract class StaticResourceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(StaticResourceHandler.class); private String webRoot = "webroot/"; public void setWebRoot(String webRoot) { this.webRoot = webRoot; } protected abstract Part findResource(String path) throws IOException; public Response handle(String path) { path = URI.create(webRoot + path).normalize().getPath(); if (!path.startsWith(webRoot)) { // maybe request of attack, just return 404 return Response.failResp(new InvocationException(Status.NOT_FOUND, Status.NOT_FOUND.getReasonPhrase())); } Part part; try { part = findResource(path); } catch (Throwable e) { LOGGER.error("failed to process static resource, path={}", path, e); return Response .failResp(new InvocationException(Status.INTERNAL_SERVER_ERROR, "failed to process static resource.")); } if (part == null) { return Response.failResp(new InvocationException(Status.NOT_FOUND, Status.NOT_FOUND.getReasonPhrase())); } return handler(part); } public Response handler(Part part) { // todo: cache control Response response = Response.ok(part); response.setHeader(HttpHeaders.CONTENT_TYPE, part.getContentType()); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline"); return response; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.route; import java.util.regex.Pattern; public class URLMappedConfigurationItem { private String microserviceName; private String versionRule; private int prefixSegmentCount; private Pattern pattern; private String stringPattern; public String getMicroserviceName() { return microserviceName; } public void setMicroserviceName(String microserviceName) { this.microserviceName = microserviceName; } public String getVersionRule() { return versionRule; } public void setVersionRule(String versionRule) { this.versionRule = versionRule; } public int getPrefixSegmentCount() { return prefixSegmentCount; } public void setPrefixSegmentCount(int prefixSegmentCount) { this.prefixSegmentCount = prefixSegmentCount; } public Pattern getPattern() { return pattern; } public void setPattern(Pattern pattern) { this.pattern = pattern; } public String getStringPattern() { return stringPattern; } public void setStringPattern(String stringPattern) { this.stringPattern = stringPattern; } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.route; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; public class URLMappedConfigurationLoader { private static final Logger LOG = LoggerFactory.getLogger(URLMappedConfigurationLoader.class); private static final String KEY_MAPPING_PATH = ".path"; private static final String KEY_MAPPING_SERVICE_NAME = "%s.%s.microserviceName"; private static final String KEY_MAPPING_VERSION_RULE = "%s.%s.versionRule"; private static final String KEY_MAPPING_PREFIX_SEGMENT_COUNT = "%s.%s.prefixSegmentCount"; public static Map loadConfigurations( Environment environment, String configPrefix) { Map configurations = new HashMap<>(); Set configsItems = ConfigUtil.propertiesWithPrefix((ConfigurableEnvironment) environment, configPrefix); for (String pathKey : configsItems) { if (pathKey.endsWith(KEY_MAPPING_PATH)) { URLMappedConfigurationItem configurationItem = new URLMappedConfigurationItem(); String pattern = environment.getProperty(pathKey); if (StringUtils.isEmpty(pattern)) { continue; } configurationItem.setPattern(Pattern.compile(pattern)); configurationItem.setStringPattern(pattern); String pathKeyItem = pathKey .substring(configPrefix.length() + 1, pathKey.length() - KEY_MAPPING_PATH.length()); configurationItem.setMicroserviceName(environment.getProperty( String.format(KEY_MAPPING_SERVICE_NAME, configPrefix, pathKeyItem))); if (StringUtils.isEmpty(configurationItem.getMicroserviceName())) { continue; } configurationItem.setPrefixSegmentCount(environment.getProperty( String.format(KEY_MAPPING_PREFIX_SEGMENT_COUNT, configPrefix, pathKeyItem), int.class, 0)); configurationItem.setVersionRule(environment.getProperty( String.format(KEY_MAPPING_VERSION_RULE, configPrefix, pathKeyItem), "0.0.0+")); configurations.put(pathKeyItem, configurationItem); } } logConfigurations(configurations); return configurations; } private static void logConfigurations(Map configurations) { configurations.forEach((key, item) -> LOG.info("config item: key=" + key + ";pattern=" + item.getStringPattern() + ";service=" + item.getMicroserviceName() + ";versionRule=" + item.getVersionRule())); } } ================================================ FILE: common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.route; /** * Commonly used methods in this package. */ public final class Utils { private Utils() { } /** * Get the actual path without prefix * @param path full path * @param pathIndex the index of / that after prefix * @return actual path */ public static String findActualPath(String path, int pathIndex) { if (pathIndex <= 0) { return path; } int fromIndex = 0; int counter = pathIndex; char[] chars = path.toCharArray(); for (int i = 0; i < chars.length; i++) { if (chars[i] == '/') { if (--counter < 0) { fromIndex = i; break; } } } return fromIndex > 0 ? path.substring(fromIndex) : ""; } } ================================================ FILE: common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.foundation.common.utils.json.PartModule ================================================ FILE: common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor ================================================ FILE: common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.common.rest.filter.RestFilterProvider ================================================ FILE: common/common-rest/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.common.rest.CommonRestConfiguration ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.servicecomb.common.rest.RestConst.DECODE_INVOCATION_CONTEXT; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.SCBStatus; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.json.Json; import io.vertx.ext.web.RoutingContext; public class RestProducerInvocationCreatorTest { final RoutingContext routingContext = Mockito.mock(RoutingContext.class); final MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); final ServicePathManager servicePathManager = Mockito.mock(ServicePathManager.class); final RestOperationMeta restOperationMeta = Mockito.mock(RestOperationMeta.class); final Endpoint endpoint = Mockito.mock(Endpoint.class); final HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class); final HttpServletResponseEx responseEx = Mockito.mock(HttpServletResponseEx.class); final OperationLocator locator = Mockito.mock(OperationLocator.class); final InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); final OperationMeta operationMeta = Mockito.mock(OperationMeta.class); final SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); RestProducerInvocationCreator creator; static SCBEngine engine; static Environment environment; @BeforeAll public static void beforeClass() { environment = Mockito.mock(Environment.class); engine = SCBBootstrap.createSCBEngineForTest(environment); engine.setStatus(SCBStatus.UP); LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); engine.setEnvironment(environment); } @AfterAll public static void afterClass() { engine.destroy(); } @BeforeEach public void setUp() { creator = new RestVertxProducerInvocationCreator(routingContext, microserviceMeta, endpoint, requestEx, responseEx); creator = Mockito.spy(creator); } @Test public void should_failed_when_not_defined_any_schema() { try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { mockedStatic.when(() -> ServicePathManager.getServicePathManager(null)).thenReturn(servicePathManager); InvocationException throwable = (InvocationException) catchThrowable(() -> creator.createAsync().join()); CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); assertThat(throwable.getStatusCode()).isEqualTo(NOT_FOUND.getStatusCode()); assertThat(Json.encode(data)).isIn("{\"code\":\"SCB.00000002\",\"message\":\"Not Found\"}", "{\"message\":\"Not Found\",\"code\":\"SCB.00000002\"}"); } } @Test public void should_save_requestEx_in_invocation_context() { try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Invocation invocation = creator.createAsync().join(); Object request = invocation.getLocalContext(RestConst.REST_REQUEST); assertThat(request).isSameAs(requestEx); } } @Test public void should_save_path_var_map_in_requestEx() { try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); creator.createAsync().join(); Mockito.verify(requestEx, Mockito.times(1)).setAttribute(Mockito.eq(RestConst.PATH_PARAMETERS), Mockito.any()); } } @Test public void should_merge_invocation_context_from_request() { Mockito.when(environment.getProperty(DECODE_INVOCATION_CONTEXT, boolean.class, true)) .thenReturn(true); try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); Invocation invocation = creator.createAsync().join(); assertThat(invocation.getContext("k")).isEqualTo("v"); } } @Test public void should_not_merge_invocation_context_from_request() { Mockito.when(environment.getProperty(DECODE_INVOCATION_CONTEXT, boolean.class, true)) .thenReturn(false); try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); Invocation invocation = creator.createAsync().join(); assertThat(invocation.getContext("k")).isNull(); } } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestDefPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefPath { @Test public void testPathRegExp() throws Exception { PathRegExp oPathRegExp = new PathRegExp("//{test}//"); Assertions.assertEquals(1, oPathRegExp.getGroupCount()); Assertions.assertEquals(0, oPathRegExp.getGroupWithRegExpCount()); PathRegExp oSecondPathRegExp = new PathRegExp("{[^/:]+?}"); Assertions.assertEquals(1, oSecondPathRegExp.getGroupCount()); Assertions.assertEquals(1, oSecondPathRegExp.getGroupWithRegExpCount()); Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test/")); Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test")); Assertions.assertNull(oSecondPathRegExp.match("{test/test}", null)); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestRestEngineSchemaListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.common.rest.locator.TestPathSchema; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestRestEngineSchemaListener { static SCBEngine scbEngine; static ServicePathManager spm; @BeforeAll public static void setup() { Environment environment = Mockito.mock(Environment.class); scbEngine = SCBBootstrap.createSCBEngineForTest(environment); ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); scbEngine.setTransportManager(transportManager); scbEngine.setExecutorManager(executorManager); scbEngine.setEnvironment(environment); LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); List listeners = new ArrayList<>(); listeners.add(new RestEngineSchemaListener()); scbEngine.setBootListeners(listeners); scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); } @AfterAll public static void teardown() { scbEngine.destroy(); } @Test public void testLocateNotFound() { InvocationException exception = Assertions.assertThrows(InvocationException.class, () -> spm.producerLocateOperation("/notExist", "GET")); Assertions.assertEquals("InvocationException: code=404;msg=CommonExceptionData [message=Not Found]", exception.getMessage()); } @Test public void testLocateNotFoundDynamicRemained() { InvocationException exception = Assertions.assertThrows(InvocationException.class, () -> spm.producerLocateOperation("/dynamic/1/2", "GET")); Assertions.assertEquals("InvocationException: code=404;msg=CommonExceptionData [message=Not Found]", exception.getMessage()); } @Test public void testLocateStaticMethodNotAllowed() { InvocationException exception = Assertions.assertThrows(InvocationException.class, () -> spm.producerLocateOperation("/staticEx", "POST")); Assertions.assertEquals("InvocationException: code=405;msg=CommonExceptionData [message=Method Not Allowed]", exception.getMessage()); } @Test public void testLocateDynamicMethodNotAllowed() { InvocationException exception = Assertions.assertThrows(InvocationException.class, () -> spm.producerLocateOperation("/dynamic/1", "POST")); Assertions.assertEquals("InvocationException: code=405;msg=CommonExceptionData [message=Method Not Allowed]", exception.getMessage()); } @Test public void testLocateStaticFound() { Assertions.assertNotNull(spm.producerLocateOperation("/staticEx", "GET")); } @Test public void testLocateDynamicFound() { OperationLocator locator = spm.producerLocateOperation("/dynamic/1", "GET"); Assertions.assertEquals("1", locator.getPathVarMap().get("id")); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/PojoModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec; public class PojoModel { private String name; private String desc; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessor; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.Parameter; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public class TestRestCodec { private static RestOperationMeta restOperation; private static List paramList = null; static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); Parameter hp = new HeaderParameter(); hp.setName("header"); hp.setSchema(new Schema<>()); RestParam restParam = new RestParam(null, hp, int.class); restOperation = Mockito.mock(RestOperationMeta.class); paramList = new ArrayList<>(); paramList.add(restParam); Mockito.when(restOperation.getParamList()).thenReturn(paramList); Mockito.when(restOperation.getParamByName("test")).thenReturn(restParam); } @AfterAll public static void afterClass() { restOperation = null; paramList.clear(); } @Test public void testRestToArgs() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); RestOperationMeta restOperation = Mockito.mock(RestOperationMeta.class); RestParam restParam = Mockito.mock(RestParam.class); ParamValueProcessor processor = Mockito.mock(ParamValueProcessor.class); List params = new ArrayList<>(); params.add(restParam); String s = "my"; Mockito.when(restOperation.getParamList()).thenReturn(params); Mockito.when(restParam.getParamProcessor()).thenReturn(processor); Mockito.when(processor.getValue(request)).thenReturn(s); Mockito.when(restParam.getParamName()).thenReturn("test"); Map xx = RestCodec.restToArgs(request, restOperation); Assertions.assertEquals(xx.get("test"), s); } @Test public void testRestToArgsException() throws Exception { ParamValueProcessor processor = Mockito.mock(ParamValueProcessor.class); Mockito.when(processor.getValue(Mockito.any())).thenThrow(new Exception("bad request parame")); RestOperationMeta restOperation = Mockito.mock(RestOperationMeta.class); RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(restParam.getParamProcessor()).thenReturn(processor); List params = new ArrayList<>(); params.add(restParam); Mockito.when(restOperation.getParamList()).thenReturn(params); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(restOperation.getOperationMeta()).thenReturn(operationMeta); boolean success = false; try { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); RestCodec.restToArgs(request, restOperation); success = true; } catch (InvocationException e) { Assertions.assertEquals(400, e.getStatusCode()); Assertions.assertTrue(e.getMessage().contains("Parameter is not valid")); } Assertions.assertFalse(success); } @Test public void testRestToArgsInstanceException() throws Exception { InvocationException exception = new InvocationException(Status.BAD_REQUEST, "Parameter is not valid."); ParamValueProcessor processor = Mockito.mock(ParamValueProcessor.class); Mockito.when(processor.getValue(Mockito.any())).thenThrow(exception); RestOperationMeta restOperation = Mockito.mock(RestOperationMeta.class); RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(restParam.getParamProcessor()).thenReturn(processor); List params = new ArrayList<>(); params.add(restParam); Mockito.when(restOperation.getParamList()).thenReturn(params); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(restOperation.getOperationMeta()).thenReturn(operationMeta); boolean success = false; try { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); RestCodec.restToArgs(request, restOperation); success = true; } catch (InvocationException e) { Assertions.assertEquals(e.getStatusCode(), Status.BAD_REQUEST.getStatusCode()); Assertions.assertTrue(((CommonExceptionData) e.getErrorData()).getMessage() .contains("Parameter is not valid for operation")); } Assertions.assertFalse(success); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Date; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.exc.StreamConstraintsException; import com.google.common.base.Strings; import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.core.JsonParser.Feature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.type.TypeFactory; import io.vertx.core.json.JsonObject; import org.junit.jupiter.api.Test; public class TestRestObjectMapper { @Test public void testFormatDate() throws Exception { RestObjectMapper mapper = new RestObjectMapper(); // must read/write ISO 8061 dates Date date = mapper.readValue("\"2017-07-21T17:32:28Z\"".getBytes(), Date.class); String dateStr = mapper.writeValueAsString(date); Assertions.assertEquals(dateStr, "\"2017-07-21T17:32:28.000+00:00\""); date = mapper.readValue("\"2017-07-21T17:32:28.320+0100\"".getBytes(), Date.class); dateStr = mapper.writeValueAsString(date); Assertions.assertEquals(dateStr, "\"2017-07-21T16:32:28.320+00:00\""); // one hour later } @Test public void testAutoCloseSource() { Assertions.assertFalse(RestObjectMapperFactory.getRestObjectMapper().getFactory().isEnabled(Feature.AUTO_CLOSE_SOURCE)); } @Test public void testDeserializationFeature() { Assertions.assertFalse( RestObjectMapperFactory.getRestObjectMapper().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); } @Test public void testDefaultViewInclusionFeature() { Assertions.assertFalse(RestObjectMapperFactory.getRestObjectMapper().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)); } @Test public void testJsonObjectWork() { JsonObject obj = new JsonObject(); obj.put("name", "a"); obj.put("desc", "b"); PojoModel model = RestObjectMapperFactory.getRestObjectMapper() .convertValue(obj, TypeFactory.defaultInstance().constructType(PojoModel.class)); Assertions.assertEquals("a", model.getName()); Assertions.assertEquals("b", model.getDesc()); RestObjectMapperFactory.setDefaultRestObjectMapper(new RestObjectMapper()); model = RestObjectMapperFactory.getRestObjectMapper() .convertValue(obj, TypeFactory.defaultInstance().constructType(PojoModel.class)); Assertions.assertEquals("a", model.getName()); Assertions.assertEquals("b", model.getDesc()); InputStream inputStream = new ByteArrayInputStream(new byte[0]); try { RestObjectMapperFactory.getRestObjectMapper().readValue(inputStream, PojoModel.class); Assertions.fail(); } catch (MismatchedInputException e) { // right place, nothing to do. } catch (Exception e) { Assertions.fail(); } } @Test public void testReadValue() { String content = "{\"desc\":" + Strings.repeat("9", 1001) + "}"; try { RestObjectMapperFactory.getRestObjectMapper().readValue(content, PojoModel.class); Assertions.fail(); } catch (StreamConstraintsException e) { // right place, nothing to do. } catch (JsonProcessingException ex) { Assertions.fail(); } } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.fix; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.Field; import java.util.concurrent.Callable; import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.core.exc.InputCoercionException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.google.common.base.Strings; import org.junit.jupiter.api.Test; public class TestDoSFix { static final ObjectMapper mapper = new RestObjectMapper(); static final String invalidNum = Strings.repeat("9", 1000); static final String invalidStr = "\"" + invalidNum + "\""; static final String invalidArrNum = "[" + invalidNum + "]"; static final String invalidArrStr = "[\"" + invalidNum + "\"]"; public static class Model { public Color color; public char cValue; public Character cObjValue; public byte bValue; public Byte bObjValue; public short sValue; public Short sObjValue; public int iValue; public Integer iObjValue; public long lValue; public Long lObjValue; public float fValue; public Float fObjValue; public double dValue; public Double dObjValue; } void fastFail(Callable callable, Class eCls) { long start = System.currentTimeMillis(); try { Object ret = callable.call(); Assertions.fail("expect failed, but success to be " + ret); } catch (AssertionError e) { throw e; } catch (Throwable e) { if (eCls != e.getClass()) { e.printStackTrace(); } Assertions.assertEquals(eCls, e.getClass()); } long time = System.currentTimeMillis() - start; Assertions.assertTrue(time < 1000, "did not fix DoS problem, time:" + time); } void fastFail(String input, Class cls, Class eCls) { fastFail(() -> mapper.readValue(input, cls), eCls); fastFail(() -> mapper.readValue(new ByteArrayInputStream(input.getBytes()), cls), eCls); } void batFastFail(Class cls, Class e1, Class e2) { fastFail(invalidNum, cls, e1); fastFail(invalidStr, cls, e2); fastFail(invalidArrNum, cls, e1); fastFail(invalidArrStr, cls, e2); } void batFastFail(Class cls) { batFastFail(cls, InputCoercionException.class, InvalidFormatException.class); } void batFastFail(String fieldName, Class e1, Class e2) { fastFail("{\"" + fieldName + "\":" + invalidNum + "}", Model.class, e1); fastFail("{\"" + fieldName + "\":\"" + invalidNum + "\"}", Model.class, e2); fastFail("{\"" + fieldName + "\":[" + invalidNum + "]}", Model.class, e1); fastFail("{\"" + fieldName + "\":[\"" + invalidNum + "\"]}", Model.class, e2); } void batFastFail(String fieldName) { batFastFail(fieldName, JsonMappingException.class, InvalidFormatException.class); } @Test public void testEnum() { batFastFail(Color.class); batFastFail("color"); } @Test public void testChar() { batFastFail(char.class, InputCoercionException.class, InvalidFormatException.class); batFastFail(Character.class, InputCoercionException.class, InvalidFormatException.class); batFastFail("cValue", JsonMappingException.class, InvalidFormatException.class); batFastFail("cObjValue", JsonMappingException.class, InvalidFormatException.class); } @Test public void testByte() { batFastFail(byte.class); batFastFail(Byte.class); batFastFail("bValue"); batFastFail("bObjValue"); } @Test public void testShort() { batFastFail(short.class); batFastFail(Short.class); batFastFail("sValue"); batFastFail("sObjValue"); } @Test public void testInt() { batFastFail(int.class); batFastFail(Integer.class); batFastFail("iValue"); batFastFail("iObjValue"); } @Test public void testLong() { batFastFail(long.class); batFastFail(Long.class); batFastFail("lValue"); batFastFail("lObjValue"); } Object fastSucc(Callable callable) { long start = System.currentTimeMillis(); try { Object ret = callable.call(); Assertions.assertTrue(System.currentTimeMillis() - start < 1000); return ret; } catch (Throwable e) { throw new IllegalStateException(e); } } Object fastSucc(String input, Class cls) { return fastSucc(() -> mapper.readValue(input, cls)); } Object fastSucc(InputStream input, Class cls) { return fastSucc(() -> { input.reset(); return mapper.readValue(input, cls); }); } void batFastSucc(Class cls, Object expected) { Assertions.assertEquals(expected, fastSucc(invalidNum, cls)); Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidNum.getBytes()), cls)); Assertions.assertEquals(expected, fastSucc(invalidStr, cls)); Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidStr.getBytes()), cls)); Assertions.assertEquals(expected, fastSucc(invalidArrNum, cls)); Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidArrNum.getBytes()), cls)); Assertions.assertEquals(expected, fastSucc(invalidArrStr, cls)); Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidArrStr.getBytes()), cls)); } void checkField(Model model, String fieldName, Object expected) { try { Field field = Model.class.getField(fieldName); Object value = field.get(model); Assertions.assertEquals(expected, value); } catch (Throwable e) { throw new IllegalStateException(e); } } void batFastSucc(String fieldName, Object expected) { checkField((Model) fastSucc("{\"" + fieldName + "\":" + invalidNum + "}", Model.class), fieldName, expected); checkField((Model) fastSucc("{\"" + fieldName + "\":\"" + invalidNum + "\"}", Model.class), fieldName, expected); checkField((Model) fastSucc("{\"" + fieldName + "\":[" + invalidNum + "]}", Model.class), fieldName, expected); checkField((Model) fastSucc("{\"" + fieldName + "\":[\"" + invalidNum + "\"]}", Model.class), fieldName, expected); } @Test public void testFloat() { batFastSucc(float.class, Float.POSITIVE_INFINITY); batFastSucc(Float.class, Float.POSITIVE_INFINITY); batFastSucc("fValue", Float.POSITIVE_INFINITY); batFastSucc("fObjValue", Float.POSITIVE_INFINITY); } @Test public void testDouble() { batFastSucc(double.class, Double.POSITIVE_INFINITY); batFastSucc(Double.class, Double.POSITIVE_INFINITY); batFastSucc("dValue", Double.POSITIVE_INFINITY); batFastSucc("dObjValue", Double.POSITIVE_INFINITY); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.BodyProcessor; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.parameters.RequestBody; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.impl.BufferImpl; import io.vertx.core.http.impl.headers.HeadersMultiMap; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; public class TestBodyProcessor { Environment environment = Mockito.mock(Environment.class); final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); MultiMap headers; final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); ParamValueProcessor processor; final Buffer inputBodyByteBuf = Buffer.buffer(); final BufferInputStream inputStream = new BufferInputStream(inputBodyByteBuf); Buffer outputBodyBuffer; String value; private void createProcessor(Class type) { OperationMeta operationMeta = Mockito.mock(OperationMeta.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); OpenAPI openAPI = Mockito.mock(OpenAPI.class); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getSwagger()).thenReturn(openAPI); RequestBody requestBody = Mockito.mock(RequestBody.class); Content content = Mockito.mock(Content.class); Mockito.when(requestBody.getContent()).thenReturn(content); Mockito.when(requestBody.getRequired()).thenReturn(true); Set supported = new HashSet<>(); supported.add(MediaType.APPLICATION_JSON); supported.add(MediaType.TEXT_PLAIN); Mockito.when(content.keySet()).thenReturn(supported); processor = new BodyProcessor(operationMeta, TypeFactory.defaultInstance().constructType(type), requestBody); } private void createRawJsonProcessor() { processor = new RawJsonBodyProcessor(TypeFactory.defaultInstance().constructType(String.class), true); } private void createClientRequest() { Mockito.doAnswer(invocation -> { headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); return null; }).when(clientRequest).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); Mockito.when(clientRequest.getHeaders()).thenReturn(headers); } private void initInputStream() throws IOException { Mockito.when(request.getInputStream()).thenReturn(inputStream); } private void setupGetValue(Class type) throws IOException { createProcessor(type); initInputStream(); } @BeforeEach public void before() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); headers = HeadersMultiMap.httpHeaders(); value = "value"; } @Test public void testGetValueHaveAttr() throws Exception { int body = 10; createProcessor(String.class); Mockito.when(request.getAttribute(RestConst.BODY_PARAMETER)).thenReturn(body); Object result = processor.getValue(request); Assertions.assertEquals("10", result); } @Test public void testGetValueNoAttrNoStream() throws Exception { createProcessor(String.class); Mockito.when(request.getInputStream()).thenReturn(null); Assertions.assertThrows(InvocationException.class, () -> processor.getValue(request)); } @Test public void testGetValueTextPlain() throws Exception { setupGetValue(String.class); inputBodyByteBuf.appendString("abc", StandardCharsets.UTF_8.toString()); Mockito.when(request.getContentType()).thenReturn(MediaType.TEXT_PLAIN); Assertions.assertEquals("abc", processor.getValue(request)); } @Test public void testGetValueContextTypeJson() throws Exception { setupGetValue(Integer.class); inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Mockito.when(request.getContentType()).thenReturn(MediaType.APPLICATION_JSON); Assertions.assertEquals(1, processor.getValue(request)); } @Test public void testGetValueDefaultJson() throws Exception { setupGetValue(Integer.class); inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Assertions.assertEquals(1, processor.getValue(request)); } @Test public void testSetValue() throws Exception { createClientRequest(); createProcessor(String.class); Mockito.when(clientRequest.getHeaders()).thenReturn(headers); ParamValueProcessor spy = Mockito.spy(processor); Mockito.doAnswer(invocation -> { outputBodyBuffer = new BufferImpl().appendBytes((value).getBytes(StandardCharsets.UTF_8)); return null; }).when(spy).setValue(clientRequest, value); spy.setValue(clientRequest, value); processor.setValue(clientRequest, value); Assertions.assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.CONTENT_TYPE)); Assertions.assertEquals(value, outputBodyBuffer.toString()); } @Test public void testSetValueTextPlain() throws Exception { createClientRequest(); createProcessor(String.class); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); ParamValueProcessor spy = Mockito.spy(processor); Mockito.doAnswer(invocation -> { outputBodyBuffer = new BufferImpl().appendBytes((value).getBytes(StandardCharsets.UTF_8)); return null; }).when(spy).setValue(clientRequest, value); spy.setValue(clientRequest, value); processor.setValue(clientRequest, value); Assertions.assertEquals(MediaType.TEXT_PLAIN, headers.get(HttpHeaders.CONTENT_TYPE)); Assertions.assertEquals(value, outputBodyBuffer.toString()); } @Test public void testGetParameterPath() { createProcessor(String.class); Assertions.assertEquals("", processor.getParameterPath()); } @Test public void testGetProcessorType() { createProcessor(String.class); Assertions.assertEquals("body", processor.getProcessorType()); } @Test public void testGetValueRawJson() throws Exception { createRawJsonProcessor(); initInputStream(); inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Assertions.assertEquals("\"1\"", processor.getValue(request)); } @Test public void testGetValueRawJsonHaveAttr() throws Exception { int body = 10; createRawJsonProcessor(); Mockito.when(request.getAttribute(RestConst.BODY_PARAMETER)).thenReturn(body); Object result = processor.getValue(request); Assertions.assertEquals("10", result); } @Test public void testGetValueRawJsonNoAttrNoStream() throws Exception { createRawJsonProcessor(); Mockito.when(request.getInputStream()).thenReturn(null); Object result = processor.getValue(request); Assertions.assertNull(result); } @Test public void testSetValueRawJson() throws Exception { createClientRequest(); createRawJsonProcessor(); ParamValueProcessor spy = Mockito.spy(processor); Mockito.doAnswer(invocation -> { outputBodyBuffer = new BufferImpl().appendBytes((value).getBytes(StandardCharsets.UTF_8)); return null; }).when(spy).setValue(clientRequest, value); spy.setValue(clientRequest, value); processor.setValue(clientRequest, value); Assertions.assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.CONTENT_TYPE)); Assertions.assertEquals("value", outputBodyBuffer.toString()); } static class BodyModel { public String name; public int age; } @Test public void convertFromFormData() throws Exception { createProcessor(BodyModel.class); Map parameterMap = new HashMap<>(); parameterMap.put("name", new String[] {"n"}); parameterMap.put("age", new String[] {"10"}); Mockito.when(request.getParameterMap()).thenReturn(parameterMap); Mockito.when(request.getContentType()).thenReturn(MediaType.MULTIPART_FORM_DATA + ";utf-8"); BodyModel bm = (BodyModel) processor.getValue(request); Assertions.assertEquals("n", bm.name); Assertions.assertEquals(10, bm.age); } @Test public void convertFromUrlencoded() throws Exception { createProcessor(BodyModel.class); Map parameterMap = new HashMap<>(); parameterMap.put("name", new String[] {"n"}); parameterMap.put("age", new String[] {"10"}); Mockito.when(request.getParameterMap()).thenReturn(parameterMap); Mockito.when(request.getContentType()).thenReturn(MediaType.APPLICATION_FORM_URLENCODED + ";utf-8"); BodyModel bm = (BodyModel) processor.getValue(request); Assertions.assertEquals("n", bm.name); Assertions.assertEquals(10, bm.age); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.util.HashMap; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.BodyProcessor; import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.RequestBody; @SuppressWarnings({"rawtypes", "unchecked"}) public class TestBodyProcessorCreator { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void before() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); } @Test public void testCreateNormal() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(BodyProcessorCreator.PARAM_TYPE); RequestBody param = new RequestBody(); param.setContent(new Content()); param.getContent().addMediaType(SwaggerConst.DEFAULT_MEDIA_TYPE, new MediaType()); param.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE).setSchema(new Schema()); param.setExtensions(new HashMap<>()); ParamValueProcessor processor = creator.create(null, null, param, String.class); Assertions.assertEquals(BodyProcessor.class, processor.getClass()); } @Test public void testCreateRawJson() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(BodyProcessorCreator.PARAM_TYPE); RequestBody param = new RequestBody(); param.setContent(new Content()); param.getContent().addMediaType(SwaggerConst.DEFAULT_MEDIA_TYPE, new MediaType()); param.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE).setSchema(new Schema()); param.addExtension(SwaggerConst.EXT_RAW_JSON_TYPE, true); ParamValueProcessor processor = creator.create(null, null, param, String.class); Assertions.assertEquals(RawJsonBodyProcessor.class, processor.getClass()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.util.Date; import java.util.HashMap; import java.util.Map; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.CookieProcessorCreator.CookieProcessor; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.StdDateFormat; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestCookieProcessor { final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); final Map cookies = new HashMap<>(); final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); final String cookieName = "v1"; final String cookieValue = "c1v"; private CookieProcessor createProcessor(String name, Class type) { return new CookieProcessor(name, TypeFactory.defaultInstance().constructType(type), null, true); } private CookieProcessor createProcessor(String name, Class type, String defaultValue, boolean required) { return new CookieProcessor(name, TypeFactory.defaultInstance().constructType(type), defaultValue, required); } private void createClientRequest() { Mockito.doAnswer(invocation -> { cookies.put(cookieName, cookieValue); return null; }).when(clientRequest).addCookie(cookieName, cookieValue); } @Test public void testGetValueNoCookies() throws Exception { Mockito.when(request.getCookies()).thenReturn(null); CookieProcessor processor = createProcessor(cookieName, String.class, null, false); Object value = processor.getValue(request); Assertions.assertNull(value); } @Test public void testNoCookieAndRequired() throws Exception { Mockito.when(request.getCookies()).thenReturn(null); CookieProcessor processor = createProcessor(cookieName, String.class, null, true); try { processor.getValue(request); Assertions.assertEquals("required is true, throw exception", "not throw exception"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("Parameter is required.")); } } @Test public void testGetValueCookiesNotFound() throws Exception { Cookie[] cookies = new Cookie[] {new Cookie(cookieName, cookieValue)}; Mockito.when(request.getCookies()).thenReturn(cookies); CookieProcessor processor = createProcessor("c2", String.class, null, false); Object value = processor.getValue(request); Assertions.assertNull(value); } @Test public void testGetValueCookiesFound() throws Exception { Cookie[] cookies = new Cookie[] {new Cookie(cookieName, cookieValue)}; Mockito.when(request.getCookies()).thenReturn(cookies); CookieProcessor processor = createProcessor(cookieName, String.class); Object value = processor.getValue(request); Assertions.assertEquals(cookieValue, value); } @Test public void testGetValueRequiredTrue() throws Exception { Cookie[] cookies = new Cookie[] {new Cookie(cookieName, null)}; Mockito.when(request.getCookies()).thenReturn(cookies); CookieProcessor processor = createProcessor(cookieName, String.class, null, true); try { processor.getValue(request); Assertions.assertEquals("required is true, throw exception", "not throw exception"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("Parameter is required.")); } } @Test public void testGetValueRequiredFalse() throws Exception { Cookie[] cookies = new Cookie[] {new Cookie(cookieName, null)}; Mockito.when(request.getCookies()).thenReturn(cookies); CookieProcessor processor = createProcessor(cookieName, String.class, "test", false); Object result = processor.getValue(request); Assertions.assertEquals("test", result); } @SuppressWarnings("deprecation") @Test public void testGetValueCookiesDate() throws Exception { Date date = new Date(); String strDate = com.fasterxml.jackson.databind.util.ISO8601Utils.format(date); Cookie[] cookies = new Cookie[] {new Cookie(cookieName, strDate)}; Mockito.when(request.getCookies()).thenReturn(cookies); CookieProcessor processor = createProcessor(cookieName, Date.class); Object value = processor.getValue(request); Assertions.assertEquals(strDate, com.fasterxml.jackson.databind.util.ISO8601Utils.format((Date) value)); } @Test public void testSetValue() throws Exception { createClientRequest(); CookieProcessor processor = createProcessor(cookieName, String.class); processor.setValue(clientRequest, cookieValue); Assertions.assertEquals(cookieValue, cookies.get(cookieName)); } @Test public void testSetValueDateFixed() throws Exception { Date date = new Date(1586957400199L); String strDate = "2020-04-15T13:30:00.199+00:00"; String cookieValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date); Mockito.doAnswer(invocation -> { cookies.put("h1", cookieValue); return null; }).when(clientRequest).addCookie("h1", cookieValue); CookieProcessor processor = createProcessor("h1", Date.class); processor.setValue(clientRequest, date); Assertions.assertEquals(strDate, cookies.get("h1")); } @Test public void testSetValueDate() throws Exception { Date date = new Date(); String strDate = new StdDateFormat().format(date); String cookieValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date); Mockito.doAnswer(invocation -> { cookies.put("h1", cookieValue); return null; }).when(clientRequest).addCookie("h1", cookieValue); CookieProcessor processor = createProcessor("h1", Date.class); processor.setValue(clientRequest, date); Assertions.assertEquals(strDate, cookies.get("h1")); } @Test public void testGetProcessorType() { CookieProcessor processor = createProcessor(cookieName, String.class); Assertions.assertEquals("cookie", processor.getProcessorType()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.CookieProcessorCreator.CookieProcessor; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.CookieParameter; public class TestCookieProcessorCreator { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); } @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(CookieProcessorCreator.PARAMTYPE); CookieParameter p = new CookieParameter(); p.setName("p1"); p.setSchema(new Schema()); ParamValueProcessor processor = creator.create(null, p.getName(), p, String.class); Assertions.assertEquals(CookieProcessor.class, processor.getClass()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.FormProcessor; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.RequestBody; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.MediaType; public class TestFormProcessor { final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); final Map forms = new HashMap<>(); final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); private FormProcessor createProcessor(String name, Type type) { return createProcessor(name, type, null, true); } private FormProcessor createProcessor(String name, Type type, String defaultValue, boolean required) { JavaType javaType = TypeFactory.defaultInstance().constructType(type); RequestBody formParameter = new RequestBody(); Content content = new Content(); MapSchema schema = new MapSchema(); io.swagger.v3.oas.models.media.MediaType mediaType = new io.swagger.v3.oas.models.media.MediaType(); if (javaType.isContainerType()) { Schema propertySchema = new ArraySchema(); schema.addProperty(name, propertySchema); mediaType.schema(schema); } else { Schema propertySchema = new Schema(); propertySchema.setDefault(defaultValue); schema.addProperty(name, propertySchema); mediaType.schema(schema); } content.addMediaType(MediaType.APPLICATION_FORM_URLENCODED, mediaType); formParameter.content(content) .required(required); return new FormProcessor(name, formParameter, MediaType.APPLICATION_FORM_URLENCODED, javaType); } @Test public void testGetValueWithAttr() throws Exception { Map forms = new HashMap<>(); forms.put("name", "value"); Mockito.when(request.getAttribute(RestConst.BODY_PARAMETER)).thenReturn(forms); ParamValueProcessor processor = createProcessor("name", String.class); Object value = processor.getValue(request); Assertions.assertEquals("value", value); } @Test public void testGetValueNormal() throws Exception { Mockito.when(request.getParameter("name")).thenReturn("value"); ParamValueProcessor processor = createProcessor("name", String.class); Object value = processor.getValue(request); Assertions.assertEquals("value", value); } @SuppressWarnings("deprecation") @Test public void testGetValueNormalDate() throws Exception { Date date = new Date(); String strDate = com.fasterxml.jackson.databind.util.ISO8601Utils.format(date); Mockito.when(request.getParameter("name")).thenReturn(strDate); ParamValueProcessor processor = createProcessor("name", Date.class); Object value = processor.getValue(request); Assertions.assertEquals(strDate, com.fasterxml.jackson.databind.util.ISO8601Utils.format((Date) value)); } @Test public void testGetValueContainerTypeNull() throws Exception { Mockito.when(request.getParameterValues("name")).thenReturn(null); ParamValueProcessor processor = createProcessor("name", String[].class, null, false); String[] value = (String[]) processor.getValue(request); Assertions.assertNull(value); } @Test public void testGetValueNull() throws Exception { Mockito.when(request.getParameter("name")).thenReturn(null); ParamValueProcessor processor = createProcessor("name", String.class, null, true); try { processor.getValue(request); Assertions.assertEquals("required is true, throw exception", "not throw exception"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("Parameter is required.")); } } @Test public void testGetValueArray() throws Exception { Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value"}); ParamValueProcessor processor = createProcessor("name", String[].class); String[] value = (String[]) processor.getValue(request); MatcherAssert.assertThat(value, Matchers.arrayContaining("value")); } @SuppressWarnings("unchecked") @Test public void testGetValueList() throws Exception { Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value"}); ParamValueProcessor processor = createProcessor("name", TypeFactory.defaultInstance().constructCollectionType(List.class, String.class), null, true); Object value = processor.getValue(request); MatcherAssert.assertThat((List) value, Matchers.contains("value")); } @SuppressWarnings("unchecked") @Test public void testGetValueSet() throws Exception { Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value"}); ParamValueProcessor processor = createProcessor("name", TypeFactory.defaultInstance().constructCollectionType(Set.class, String.class), null, true); Object value = processor.getValue(request); MatcherAssert.assertThat((Set) value, Matchers.contains("value")); } @Test public void testSetValue() throws Exception { Mockito.doAnswer(invocation -> { forms.put("name", "value"); return null; }).when(clientRequest).addForm("name", "value"); ParamValueProcessor processor = createProcessor("name", String.class); processor.setValue(clientRequest, "value"); Assertions.assertEquals("value", forms.get("name")); } @Test public void testSetValueDate() throws Exception { Date date = new Date(); Mockito.doAnswer(invocation -> { forms.put("name", date); return null; }).when(clientRequest).addForm("name", date); ParamValueProcessor processor = createProcessor("name", Date.class); processor.setValue(clientRequest, date); Assertions.assertSame(date, forms.get("name")); } @Test public void testGetProcessorType() { ParamValueProcessor processor = createProcessor("name", String.class); Assertions.assertEquals("formData", processor.getProcessorType()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.util.HashMap; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.FormProcessor; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.RequestBody; @SuppressWarnings({"rawtypes", "unchecked"}) public class TestFormProcessorCreator { @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(FormProcessorCreator.PARAMTYPE); RequestBody p = new RequestBody(); p.setContent(new Content()); p.getContent().addMediaType(SwaggerConst.FORM_MEDIA_TYPE, new MediaType()); p.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).setSchema(new Schema()); p.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().setProperties(new HashMap<>()); ParamValueProcessor processor = creator.create(null, "p1", p, String.class); Assertions.assertEquals(FormProcessor.class, processor.getClass()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.StdDateFormat; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.HeaderParameter; import jakarta.servlet.http.HttpServletRequest; public class TestHeaderProcessor { final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); final Map headers = new HashMap<>(); final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); private HeaderProcessor createProcessor(String name, Type type) { return createProcessor(name, type, null, true); } private HeaderProcessor createProcessor(String name, Type type, String defaultValue, boolean required) { JavaType javaType = TypeFactory.defaultInstance().constructType(type); HeaderParameter headerParameter = new HeaderParameter(); headerParameter.setSchema(new Schema()); headerParameter.name(name) .required(required); headerParameter.getSchema().setDefault(defaultValue); if (javaType.isContainerType()) { headerParameter.schema(new ArraySchema()); } return new HeaderProcessor(headerParameter, javaType); } Environment environment = Mockito.mock(Environment.class); @BeforeEach void setUp() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck" , boolean.class, false)).thenReturn(false); } @Test public void testGetValueNormal() throws Exception { Mockito.when(request.getHeader("h1")).thenReturn("h1v"); HeaderProcessor processor = createProcessor("h1", String.class); Object value = processor.getValue(request); Assertions.assertEquals("h1v", value); } @SuppressWarnings("deprecation") @Test public void testGetValueNormalDate() throws Exception { Date date = new Date(); String strDate = com.fasterxml.jackson.databind.util.ISO8601Utils.format(date); Mockito.when(request.getHeader("h1")).thenReturn(strDate); HeaderProcessor processor = createProcessor("h1", Date.class); Object value = processor.getValue(request); Assertions.assertEquals(strDate, com.fasterxml.jackson.databind.util.ISO8601Utils.format((Date) value)); } @Test public void testGetValueContainerTypeNull() throws Exception { Mockito.when(request.getHeader("h1")).thenReturn(null); HeaderProcessor processor = createProcessor("h1", String[].class, null, false); String[] value = (String[]) processor.getValue(request); Assertions.assertNull(value); } @Test public void testGetValueRequiredTrue() throws Exception { Mockito.when(request.getHeader("h1")).thenReturn(null); HeaderProcessor processor = createProcessor("h1", String.class); try { processor.getValue(request); Assertions.assertEquals("required is true, throw exception", "not throw exception"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("Parameter is required.")); } } @Test public void testGetValueRequiredFalse() throws Exception { Mockito.when(request.getHeader("h1")).thenReturn(null); HeaderProcessor processor = createProcessor("h1", String.class, "test", false); Object value = processor.getValue(request); Assertions.assertEquals("test", value); } @Test public void testGetValueArray() throws Exception { Mockito.when(request.getHeaders("h1")).thenReturn(Collections.enumeration(Arrays.asList("h1v"))); HeaderProcessor processor = createProcessor("h1", String[].class); String[] value = (String[]) processor.getValue(request); MatcherAssert.assertThat(value, Matchers.arrayContaining("h1v")); } @SuppressWarnings("unchecked") @Test public void testGetValueList() throws Exception { Mockito.when(request.getHeaders("h1")).thenReturn(Collections.enumeration(Arrays.asList("h1v"))); HeaderProcessor processor = createProcessor("h1", TypeFactory.defaultInstance().constructCollectionType(List.class, String.class), null, true); Object value = processor.getValue(request); MatcherAssert.assertThat((List) value, Matchers.contains("h1v")); } @SuppressWarnings("unchecked") @Test public void testGetValueSet() throws Exception { Mockito.when(request.getHeaders("h1")).thenReturn(Collections.enumeration(Arrays.asList("h1v"))); HeaderProcessor processor = createProcessor("h1", TypeFactory.defaultInstance().constructCollectionType(Set.class, String.class), null, true); Object value = processor.getValue(request); MatcherAssert.assertThat((Set) value, Matchers.contains("h1v")); } @Test public void testSetValue() throws Exception { Mockito.doAnswer(invocation -> { headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString("h1v")); return null; }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString("h1v")); HeaderProcessor processor = createProcessor("h1", String.class); processor.setValue(clientRequest, "h1v"); Assertions.assertEquals("h1v", headers.get("h1")); } @Test public void testSetValueNull() throws Exception { Mockito.doAnswer(invocation -> { headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(null)); return null; }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(null)); HeaderProcessor processor = createProcessor("h1", String.class); processor.setValue(clientRequest, null); Assertions.assertEquals(0, headers.size()); } @Test public void testSetValueDateFixed() throws Exception { Date date = new Date(1586957400199L); String strDate = "2020-04-15T13:30:00.199+00:00"; Mockito.doAnswer(invocation -> { headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); return null; }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); HeaderProcessor processor = createProcessor("h1", Date.class); processor.setValue(clientRequest, date); Assertions.assertEquals(strDate, headers.get("h1")); } @Test public void testSetValueDate() throws Exception { Date date = new Date(); String strDate = new StdDateFormat().format(date); Mockito.doAnswer(invocation -> { headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); return null; }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); HeaderProcessor processor = createProcessor("h1", Date.class); processor.setValue(clientRequest, date); Assertions.assertEquals(strDate, headers.get("h1")); } @Test public void testGetProcessorType() { HeaderProcessor processor = createProcessor("h1", String.class); Assertions.assertEquals("header", processor.getProcessorType()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.HeaderParameter; public class TestHeaderProcessorCreator { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(HeaderProcessorCreator.PARAMTYPE); HeaderParameter hp = new HeaderParameter(); hp.setName("h1"); hp.setSchema(new Schema()); ParamValueProcessor processor = creator.create(null, hp.getName(), hp, String.class); Assertions.assertEquals(HeaderProcessor.class, processor.getClass()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import java.util.HashMap; import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.param.PathProcessorCreator.PathProcessor; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.databind.type.TypeFactory; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestPathProcessor { final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); final Map pathVars = new HashMap<>(); ParamValueProcessor processor; private void createProcessor(String name, Class type) { processor = new PathProcessor(name, TypeFactory.defaultInstance().constructType(type), null, true); } private void prepareGetValue(String name, Class type) { createProcessor(name, type); Mockito.when(request.getAttribute(RestConst.PATH_PARAMETERS)).thenReturn(pathVars); } @Test public void testGetValueNoPathVars() throws Exception { createProcessor("name", String.class); Assertions.assertNull(processor.getValue(request)); } @Test public void testGetValuePathNotFound() throws Exception { prepareGetValue("name", String.class); Assertions.assertNull(processor.getValue(request)); } @Test public void testGetValuePathNormal() throws Exception { prepareGetValue("name", String.class); pathVars.put("name", "value"); Assertions.assertEquals("value", processor.getValue(request)); } @Test public void testGetSpaceEncoded() throws Exception { prepareGetValue("name", String.class); pathVars.put("name", "a%20b"); Assertions.assertEquals("a b", processor.getValue(request)); } @Test public void testGetPlus() throws Exception { prepareGetValue("name", String.class); pathVars.put("name", "a+b"); Assertions.assertEquals("a+b", processor.getValue(request)); } @Test public void testGetPercentage() throws Exception { prepareGetValue("name", String.class); pathVars.put("name", "%25%25"); Assertions.assertEquals("%%", processor.getValue(request)); } @Test public void testGetColon() throws Exception { prepareGetValue("name", String.class); pathVars.put("name", "aa:bb"); Assertions.assertEquals("aa:bb", processor.getValue(request)); } @Test public void testGetProcessorType() { createProcessor("name", String.class); Assertions.assertEquals("path", processor.getProcessorType()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.PathProcessorCreator.PathProcessor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.PathParameter; public class TestPathProcessorCreator { @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(PathProcessorCreator.PARAMTYPE); Parameter parameter = new PathParameter(); parameter.setName("path"); parameter.setSchema(new Schema()); ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); Assertions.assertEquals(PathProcessor.class, processor.getClass()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.StringSchema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; import io.swagger.v3.oas.models.parameters.QueryParameter; import jakarta.servlet.http.HttpServletRequest; public class TestQueryProcessor { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); private ParamValueProcessor createProcessor(String name, Class type, Parameter.StyleEnum style, boolean explode) { return createProcessor(name, type, null, true, style, explode); } private ParamValueProcessor createProcessor(String name, Class type, String defaultValue, boolean required, Parameter.StyleEnum style, boolean explode) { JavaType javaType = TypeFactory.defaultInstance().constructType(type); QueryParameter queryParameter = new QueryParameter(); queryParameter.name(name) .required(required) .setSchema(new StringSchema()); queryParameter.getSchema().setDefault(defaultValue); if (javaType.isContainerType()) { queryParameter.setSchema(new ArraySchema()); queryParameter.setExplode(explode); queryParameter.setStyle(style); } return new QueryProcessor(queryParameter, javaType); } @Test public void testGetValueNormal() throws Exception { Mockito.when(request.getParameter("name")).thenReturn("value"); ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); Object value = processor.getValue(request); Assertions.assertEquals("value", value); } @Test public void testGetValueContainerType() throws Exception { Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value", "value2"}); ParamValueProcessor processor = createProcessor("name", String[].class, StyleEnum.FORM, true); String[] value = (String[]) processor.getValue(request); MatcherAssert.assertThat(value, Matchers.arrayContaining("value", "value2")); } @Test public void testGetValueOnCollectionFormatIsCsv() throws Exception { Mockito.when(request.getParameter("name")).thenReturn("value2,value3"); ParamValueProcessor processor = createProcessor("name", String[].class, StyleEnum.FORM, false); String[] value = (String[]) processor.getValue(request); MatcherAssert.assertThat(value, Matchers.arrayContaining("value2", "value3")); } @Test public void testGetProcessorType() { ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); Assertions.assertEquals("query", processor.getProcessorType()); } @Test public void testGetValueRequiredTrue() throws Exception { Mockito.when(request.getParameter("name")).thenReturn(null); ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); try { processor.getValue(request); Assertions.assertEquals("required is true, throw exception", "not throw exception"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("Parameter name is required.")); } } @Test public void testGetValueRequiredFalse() throws Exception { Mockito.when(request.getParameter("name")).thenReturn(null); ParamValueProcessor processor = createProcessor("name", String.class, "test", false, StyleEnum.FORM, true); Object result = processor.getValue(request); Assertions.assertEquals("test", result); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.QueryParameter; import jakarta.servlet.http.HttpServletRequest; public class TestQueryProcessorCreator { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @Test public void testCreate() { ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(QueryProcessorCreator.PARAMTYPE); Parameter parameter = new QueryParameter(); parameter.setName("query"); parameter.setSchema(new Schema()); ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); Assertions.assertEquals(QueryProcessor.class, processor.getClass()); String result = (String) processor.convertValue("Hello", TypeFactory.defaultInstance().constructType(String.class)); Assertions.assertEquals("Hello", result); result = (String) processor.convertValue("", TypeFactory.defaultInstance().constructType(String.class)); Assertions.assertEquals("", result); result = (String) processor.convertValue(null, TypeFactory.defaultInstance().constructType(String.class)); Assertions.assertNull(result); } @SuppressWarnings("UnusedAssignment") @Test public void testCreateNullAsEmpty() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(true); ParamValueProcessorCreator creator = ParamValueProcessorCreatorManager.INSTANCE.findValue(QueryProcessorCreator.PARAMTYPE); Parameter parameter = new QueryParameter(); parameter.setName("query"); parameter.setSchema(new Schema()); ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); Assertions.assertEquals(QueryProcessor.class, processor.getClass()); Mockito.when(request.getParameter("query")).thenReturn("Hello"); String result = (String) processor.getValue(request); Assertions.assertEquals("Hello", result); Mockito.when(request.getParameter("query")).thenReturn(""); result = (String) processor.getValue(request); Assertions.assertNull(result); Mockito.when(request.getParameter("query")).thenReturn(null); result = (String) processor.convertValue(null, TypeFactory.defaultInstance().constructType(String.class)); result = (String) processor.getValue(request); Assertions.assertNull(result); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import static org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager.DEFAULT_SERIAL_CLASS; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.type.TypeFactory; import io.vertx.core.buffer.Buffer; import org.junit.jupiter.api.Test; public class TestProduceJsonProcessor { final ProduceProcessor pp = ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(); final JavaType stringType = TypeFactory.defaultInstance().constructType(String.class); @Test public void testEncodeResponseNull() throws Exception { Buffer buffer = pp.encodeResponse(null); Assertions.assertNull(buffer); ByteArrayOutputStream os = new ByteArrayOutputStream(); pp.encodeResponse(os, null); Assertions.assertEquals(0, os.size()); } @Test public void testDecodeResponseNull() throws Exception { JavaType resultType = TypeFactory.unknownType(); Object result = pp.decodeResponse(Buffer.buffer(), resultType); Assertions.assertNull(result); ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {}); try { pp.decodeResponse(is, resultType); Assertions.fail(); } catch (Exception e) { Assertions.assertTrue(e instanceof MismatchedInputException); } } @Test public void testBufferNormal() throws Exception { String value = "abc"; Buffer buffer = pp.encodeResponse(value); Assertions.assertEquals("\"abc\"", buffer.toString(StandardCharsets.UTF_8)); Object result = pp.decodeResponse(buffer, stringType); Assertions.assertEquals(value, result); } @Test public void testStreamNormal() throws Exception { String value = "abc"; ByteArrayOutputStream os = new ByteArrayOutputStream(); pp.encodeResponse(os, value); Assertions.assertEquals("\"abc\"", os.toString(StandardCharsets.UTF_8.name())); ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); Object result = pp.decodeResponse(is, stringType); Assertions.assertEquals(value, result); os.close(); is.close(); } @Test public void testSetSerializationView() { Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); pp.setSerializationView(null); Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); pp.setSerializationView(Object.class); Assertions.assertEquals(Object.class.getCanonicalName(), pp.getSerializationView()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestProduceProcessorManager { @Test public void testDefault() { Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), ProduceProcessorManager.INSTANCE.findDefaultProcessor()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.produce; import static org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager.DEFAULT_SERIAL_CLASS; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.vertx.core.buffer.Buffer; import org.junit.jupiter.api.Test; public class TestProduceTextPlainProcessor { final ProduceProcessor pp = ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(); final JavaType stringType = TypeFactory.defaultInstance().constructType(String.class); @Test public void testEncodeResponseNull() throws Exception { Buffer buffer = pp.encodeResponse(null); Assertions.assertNull(buffer); ByteArrayOutputStream os = new ByteArrayOutputStream(); pp.encodeResponse(os, null); Assertions.assertEquals(0, os.size()); } @Test public void testdecodeResponseNull() throws Exception { JavaType resultType = TypeFactory.unknownType(); Object result = pp.decodeResponse(Buffer.buffer(), resultType); Assertions.assertNull(result); ByteArrayInputStream is = new ByteArrayInputStream("\"\"".getBytes(StandardCharsets.UTF_8)); result = pp.decodeResponse(is, resultType); Assertions.assertEquals(result, ""); } @Test public void testBufferNormal() throws Exception { String value = "abc"; Buffer buffer = pp.encodeResponse(value); Assertions.assertEquals(value, buffer.toString(StandardCharsets.UTF_8)); Object result = pp.decodeResponse(buffer, stringType); Assertions.assertEquals(value, result); } @Test public void testStreamNormal() throws Exception { String value = "abc"; ByteArrayOutputStream os = new ByteArrayOutputStream(); pp.encodeResponse(os, value); Assertions.assertEquals(value, os.toString(StandardCharsets.UTF_8.name())); ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); Object result = pp.decodeResponse(is, stringType); Assertions.assertEquals(value, result); os.close(); is.close(); } @Test public void testSetSerializationView() { Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); pp.setSerializationView(null); Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); pp.setSerializationView(Object.class); Assertions.assertEquals("java.lang.Object", pp.getSerializationView()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsvTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Date; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; class QueryCodecCsvTest extends QueryCodecTestBase { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @BeforeEach void setUp() { codec = new QueryCodecCsv(); } @Nested class Encode { @Test void should_encode_date() throws Exception { should_encode("?q=1970-01-01T00%3A00%3A00.000%2B00%3A00", new Date(0)); } @Test void should_encode_single_value() throws Exception { should_encode("?q=v1", "v1"); } @Test void should_encode_empty_string() throws Exception { should_encode("?q=", ""); } @Test void should_encode_common_string() throws Exception { should_encode("?q=v1%2Cv2", "v1", "v2"); } @Test void should_encode_common_numbers() throws Exception { should_encode("?q=1%2C2", 1, 2); } @Test void should_encode_chinese_values() throws Exception { should_encode("?q=%E4%B8%AD%E6%96%87%2Cv2", "中文", "v2"); } @Test void should_encode_ignore_null() throws Exception { should_encode("?q=v1%2Cv2", "v1", null, "v2"); } @Test void should_encode_when_values_is_empty_after_ignore_null() throws Exception { should_encode("", new Object[] {null}); } } @Nested class Decode { @Test void should_decode_single_value_to_array() { should_decode("1", new int[] {1}); } @Test void should_decode_common_values_to_array() { should_decode("1,2", new int[] {1, 2}); } @Test void should_decode_null_to_array() { should_decode((String) null, new int[] {}); } @Test void should_decode_empty_string_to_number() { should_decode("", new int[] {0}); } @Test void should_decode_empty_string_to_string() { should_decode("", new String[] {""}); } @Test void should_decode_common_values_with_empty_string_to_array() { should_decode("1,,2", new int[] {1, 0, 2}); } @Test void should_decode_values_end_with_delimiter() { should_decode("1,,", new int[] {1, 0, 0}); } @Test void should_decode_values_start_with_delimiter() { should_decode(",,1", new int[] {0, 0, 1}); } } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMultiTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Date; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; class QueryCodecMultiTest extends QueryCodecTestBase { Environment environment = Mockito.mock(Environment.class); @BeforeEach void setUp() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); codec = new QueryCodecMulti(); } @Nested class Encode { @Test void should_encode_date() throws Exception { should_encode("?q=1970-01-01T00%3A00%3A00.000%2B00%3A00", new Date(0)); } @Test void should_encode_single_value() throws Exception { should_encode("?q=v1", "v1"); } @Test void should_encode_empty_string() throws Exception { should_encode("?q=", ""); } @Test void should_encode_common_string() throws Exception { should_encode("?q=v1&q=v2", "v1", "v2"); } @Test void should_encode_common_numbers() throws Exception { should_encode("?q=1&q=2", 1, 2); } @Test void should_encode_chinese_values() throws Exception { should_encode("?q=%E4%B8%AD%E6%96%87&q=v2", "中文", "v2"); } @Test void should_encode_ignore_null() throws Exception { should_encode("?q=v1&q=v2", "v1", null, "v2"); } @Test void should_encode_when_values_is_empty_after_ignore_null() throws Exception { should_encode("", new Object[] {null}); } } @Nested class Decode { @Test void should_decode_single_value_to_array() { should_decode(new String[] {"1"}, new int[] {1}); } @Test void should_decode_common_values_to_array() { should_decode(new String[] {"1", "2"}, new int[] {1, 2}); } @Test void should_decode_empty_string_to_array() { should_decode(new String[] {""}, new int[] {0}); } @Test void should_decode_common_values_with_empty_string_to_array() { should_decode(new String[] {"1", "", "2"}, new int[] {1, 0, 2}); } @Test void should_decode_single_value_to_single() { should_decode("1", 1); } @Test void should_decode_null_to_single() { should_decode((String) null, null); } } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Date; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; class QueryCodecPipesTest extends QueryCodecTestBase { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @BeforeEach void setUp() { codec = new QueryCodecPipes(); } @Nested class Encode { @Test void should_encode_date() throws Exception { should_encode("?q=1970-01-01T00%3A00%3A00.000%2B00%3A00", new Date(0)); } @Test void should_encode_single_value() throws Exception { should_encode("?q=v1", "v1"); } @Test void should_encode_empty_string() throws Exception { should_encode("?q=", ""); } @Test void should_encode_common_string() throws Exception { should_encode("?q=v1%7Cv2", "v1", "v2"); } @Test void should_encode_common_numbers() throws Exception { should_encode("?q=1%7C2", 1, 2); } @Test void should_encode_chinese_values() throws Exception { should_encode("?q=%E4%B8%AD%E6%96%87%7Cv2", "中文", "v2"); } @Test void should_encode_ignore_null() throws Exception { should_encode("?q=v1%7Cv2", "v1", null, "v2"); } @Test void should_encode_when_values_is_empty_after_ignore_null() throws Exception { should_encode("", new Object[] {null}); } } @Nested class Decode { @Test void should_decode_single_value_to_array() { should_decode("1", new int[] {1}); } @Test void should_decode_common_values_to_array() { should_decode("1|2", new int[] {1, 2}); } @Test void should_decode_null_to_array() { should_decode((String) null, new int[] {}); } @Test void should_decode_empty_string_to_number() { should_decode("", new int[] {0}); } @Test void should_decode_empty_string_to_string() { should_decode("", new String[] {""}); } @Test void should_decode_common_values_with_empty_string_to_array() { should_decode("1||2", new int[] {1, 0, 2}); } @Test void should_decode_values_end_with_delimiter() { should_decode("1||", new int[] {1, 0, 0}); } @Test void should_decode_values_start_with_delimiter() { should_decode("||1", new int[] {0, 0, 1}); } } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsvTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import java.util.Date; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class QueryCodecSsvTest extends QueryCodecTestBase { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @BeforeEach void setUp() { codec = new QueryCodecSsv(); } @Nested class Encode { @Test void should_encode_date() throws Exception { should_encode("?q=1970-01-01T00%3A00%3A00.000%2B00%3A00", new Date(0)); } @Test void should_encode_single_value() throws Exception { should_encode("?q=v1", "v1"); } @Test void should_encode_empty_string() throws Exception { should_encode("?q=", ""); } @Test void should_encode_common_string() throws Exception { should_encode("?q=v1+v2", "v1", "v2"); } @Test void should_encode_common_numbers() throws Exception { should_encode("?q=1+2", 1, 2); } @Test void should_encode_chinese_values() throws Exception { should_encode("?q=%E4%B8%AD%E6%96%87+v2", "中文", "v2"); } @Test void should_encode_ignore_null() throws Exception { should_encode("?q=v1+v2", "v1", null, "v2"); } @Test void should_encode_when_values_is_empty_after_ignore_null() throws Exception { should_encode("", new Object[] {null}); } } @Nested class Decode { @Test void should_decode_single_value_to_array() { should_decode("1", new int[] {1}); } @Test void should_decode_common_values_to_array() { should_decode("1 2", new int[] {1, 2}); } @Test void should_decode_null_to_array() { should_decode((String) null, new int[] {}); } @Test void should_decode_empty_string_to_number() { should_decode("", new int[] {0}); } @Test void should_decode_empty_string_to_string() { should_decode("", new String[] {""}); } @Test void should_decode_common_values_with_empty_string_to_array() { should_decode("1 2", new int[] {1, 0, 2}); } @Test void should_decode_values_end_with_delimiter() { should_decode("1 ", new int[] {1, 0, 0}); } @Test void should_decode_values_start_with_delimiter() { should_decode(" 1", new int[] {0, 0, 1}); } } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTestBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import static org.assertj.core.api.Assertions.assertThat; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.QueryParameter; public class QueryCodecTestBase { QueryCodec codec; final String queryName = "q"; void should_encode(String encodedValue, Object... values) throws Exception { URLPathStringBuilder builder = new URLPathStringBuilder(); codec.encode(builder, queryName, values); assertThat(builder.build()).isEqualTo(encodedValue); } void should_decode(String value, Object decodedValue) { HttpServletRequest request = new AbstractHttpServletRequest() { @Override public String getParameter(String name) { return value; } }; should_decode(request, decodedValue); } void should_decode(String[] value, Object decodedValue) { HttpServletRequest request = new AbstractHttpServletRequest() { @Override public String[] getParameterValues(String name) { return value; } }; should_decode(request, decodedValue); } private void should_decode(HttpServletRequest request, Object decodedValue) { Class targetType = decodedValue == null ? Object.class : decodedValue.getClass(); QueryParameter queryParameter = new QueryParameter(); queryParameter.setSchema(new Schema()); queryParameter.getSchema().setFormat(codec.getCodecName()); if (targetType.isArray()) { queryParameter.setSchema(new ArraySchema()); } QueryProcessor queryProcessor = new QueryProcessor(queryParameter, TypeFactory.defaultInstance().constructType(targetType)); Object values = codec.decode(queryProcessor, request); assertThat(values).isEqualTo(decodedValue); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.codec.query; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class QueryCodecsTest { static class MyQueryCodecCsv extends QueryCodecCsv { @Override public int getOrder() { return 0; } } @Test void can_override_by_customize_implement() { QueryCodecs queryCodecs = QueryCodecs.createForTest(); assertThat(queryCodecs.find(QueryCodecCsv.CODEC_NAME).getClass()) .isEqualTo(QueryCodecCsv.class); queryCodecs = new QueryCodecs(singletonList(new MyQueryCodecCsv())); assertThat(queryCodecs.find(QueryCodecCsv.CODEC_NAME).getClass()) .isEqualTo(MyQueryCodecCsv.class); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; import org.apache.servicecomb.common.rest.definition.path.QueryVarParamWriter; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.PathParameter; import io.swagger.v3.oas.models.parameters.QueryParameter; public class TestPath { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @BeforeEach public void setUp() { } @AfterEach public void tearDown() { } @Test public void testPathRegExp() throws Exception { PathRegExp oPathRegExp = new PathRegExp("//{test}//"); Assertions.assertEquals(1, oPathRegExp.getGroupCount()); Assertions.assertEquals(0, oPathRegExp.getGroupWithRegExpCount()); PathRegExp oSecondPathRegExp = new PathRegExp("{[^/:]+?}"); Assertions.assertEquals(1, oSecondPathRegExp.getGroupCount()); Assertions.assertEquals(1, oSecondPathRegExp.getGroupWithRegExpCount()); Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test/")); Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test")); Assertions.assertNull(oSecondPathRegExp.match("{test/test}", null)); Assertions.assertEquals("(]+?)/(.*)", (oSecondPathRegExp.toString())); Assertions.assertFalse(oSecondPathRegExp.isStaticPath()); Assertions.assertEquals(0, oSecondPathRegExp.getStaticCharCount()); Assertions.assertNotEquals(null, (oPathRegExp.match("//{test}//", new HashMap<>()))); // Error Scenarios new PathRegExp("//{test \t}//"); // Error Scenarios for double {{ try { new PathRegExp("//{test{"); Assertions.fail("an exception is expected!"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("A variable must not contain an extra")); } // Error Scenarios for illegal }} try { new PathRegExp("//}"); Assertions.fail("an exception is expected!"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("is only allowed as")); } // Error Scenarios for illegal ; try { new PathRegExp("//;"); Assertions.fail("an exception is expected!"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("matrix parameters are not allowed in")); } // Error Scenarios for NO } ; try { new PathRegExp("//{test"); Assertions.fail("an exception is expected!"); } catch (Exception e) { Assertions.assertTrue(e.getMessage().contains("No '}' found after")); } } @Test public void testUrlPathBuilder() throws Exception { Map paramMap = new HashMap<>(); Parameter pathParameter = new PathParameter(); pathParameter.setName("id"); pathParameter.setSchema(new Schema<>()); RestParam oRestParam = new RestParam(null, pathParameter, int.class); paramMap.put(oRestParam.getParamName(), oRestParam); Parameter queryParameter = new QueryParameter(); queryParameter.setName("q"); queryParameter.setSchema(new Schema<>()); oRestParam = new RestParam(null, queryParameter, String.class); paramMap.put(oRestParam.getParamName(), oRestParam); URLPathBuilder oURLPathBuilder = new URLPathBuilder("/root/{id}", paramMap); Map parameters = new HashMap<>(); parameters.put("id", 100); parameters.put("q", "query"); Assertions.assertEquals("/root/100?q=query", oURLPathBuilder.createRequestPath(parameters)); Assertions.assertEquals("/root/100", oURLPathBuilder.createPathString(parameters)); } @Test public void testQueryVarParamWriter() { boolean status = true; Parameter parameter = new QueryParameter(); parameter.setSchema(new Schema<>()); RestParam restParam = new RestParam(null, parameter, String.class); RestParam spy = Mockito.spy(restParam); Mockito.when(spy.getParamName()).thenReturn("queryVar"); QueryVarParamWriter writer = new QueryVarParamWriter(spy); try { Map parameters = new HashMap<>(); parameters.put("queryVar", "T"); verify(writer, parameters, "?queryVar=T"); parameters.put("queryVar", null); verify(writer, parameters, ""); parameters.put("queryVar", new String[] {"a", "b"}); verify(writer, parameters, "?queryVar=a&queryVar=b"); parameters.put("queryVar", new String[] {"a", null, "b"}); verify(writer, parameters, "?queryVar=a&queryVar=b"); parameters.put("queryVar", Arrays.asList("Lars", "Simon")); verify(writer, parameters, "?queryVar=Lars&queryVar=Simon"); parameters.put("queryVar", "测试"); verify(writer, parameters, "?queryVar=%E6%B5%8B%E8%AF%95"); parameters.put("queryVar", "a b"); verify(writer, parameters, "?queryVar=a+b"); parameters.put("queryVar", "a+b"); verify(writer, parameters, "?queryVar=a%2Bb"); } catch (Exception e) { status = false; } Assertions.assertTrue(status); } private void verify(QueryVarParamWriter writer, Map args, String expect) throws Exception { URLPathStringBuilder sb = new URLPathStringBuilder(); writer.write(sb, args); Assertions.assertEquals(expect, sb.build()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationComparator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import org.apache.servicecomb.common.rest.locator.MicroservicePaths; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRestOperationComparator { @Test public void testStaticCharCount() { RestOperationMeta less = new RestOperationMeta(); less.setAbsolutePath("/a/{id}"); RestOperationMeta more = new RestOperationMeta(); more.setAbsolutePath("/abc/{id}"); MicroservicePaths paths = new MicroservicePaths(); paths.addResource(less); paths.addResource(more); paths.sortPath(); Assertions.assertSame(more, paths.getDynamicPathOperationList().get(0)); Assertions.assertSame(less, paths.getDynamicPathOperationList().get(1)); } @Test public void testVarGroupCount() { RestOperationMeta less = new RestOperationMeta(); less.setAbsolutePath("/ab/{id}"); RestOperationMeta more = new RestOperationMeta(); more.setAbsolutePath("/a/{test}/{id}"); MicroservicePaths paths = new MicroservicePaths(); paths.addResource(less); paths.addResource(more); paths.sortPath(); Assertions.assertSame(more, paths.getDynamicPathOperationList().get(0)); Assertions.assertSame(less, paths.getDynamicPathOperationList().get(1)); } @Test public void testGroupWithRegExpCount() { RestOperationMeta less = new RestOperationMeta(); less.setAbsolutePath("/a/{test}/{id}"); RestOperationMeta more = new RestOperationMeta(); more.setAbsolutePath("/a/{test : .+}/{id}"); MicroservicePaths paths = new MicroservicePaths(); paths.addResource(less); paths.addResource(more); paths.sortPath(); Assertions.assertSame(more, paths.getDynamicPathOperationList().get(0)); Assertions.assertSame(less, paths.getDynamicPathOperationList().get(1)); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.hamcrest.core.Is.is; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.common.rest.RestEngineSchemaListener; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.transport.TransportManager; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.servers.Server; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; public class TestRestOperationMeta { @Path("/") static class RestOperationMetaSchema { @Path("/emptyProduces") @GET @Produces(value = MediaType.APPLICATION_JSON) public String emptyProduces() { return null; } @Path("/emptyProducesWithView") @GET @Produces(value = MediaType.APPLICATION_JSON) @JsonView(Object.class) public String emptyProducesWithView() { return null; } @Path("/notSupport") @GET @Produces(value = MediaType.APPLICATION_JSON) public void notSupport() { } @Path("/notSupportWithView") @GET @Produces(value = MediaType.APPLICATION_JSON) @JsonView(Object.class) public void notSupportWithView() { } @Path("/textPlain") @GET @Produces(MediaType.TEXT_PLAIN) public String textPlain() { return null; } @Path("/textPlainWithView") @GET @Produces(MediaType.TEXT_PLAIN) @JsonView(Object.class) public String textPlainWithView() { return null; } @Path("/json") @GET @Produces(MediaType.APPLICATION_JSON) public String json() { return null; } @Path("/jsonWithView") @GET @Produces(MediaType.APPLICATION_JSON) @JsonView(Object.class) public String jsonWithView() { return null; } @Path("/textCharJsonChar") @GET @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public void textCharJsonChar() { } @Path("/textCharJsonCharWithView") @GET @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) @JsonView(Object.class) public void textCharJsonCharWithView() { } @Path("/download") @GET @Produces(MediaType.APPLICATION_JSON) public File download() { return null; } @Path("/downloadWithView") @GET @Produces(MediaType.APPLICATION_JSON) @JsonView(Object.class) public File downloadWithView() { return null; } @Path("/form") @POST public void form(@FormParam("form") String form) { } @Path("/formWithView") @POST public void formWithView(@FormParam("form") String form) { } } SCBEngine scbEngine; OpenAPI swagger; OperationMeta meta; RestOperationMeta operationMeta; @BeforeEach public void setUp() { Environment environment = Mockito.mock(Environment.class); scbEngine = SCBBootstrap.createSCBEngineForTest(environment); ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); scbEngine.setTransportManager(transportManager); scbEngine.setExecutorManager(executorManager); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) .thenReturn("test"); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); List listeners = new ArrayList<>(); listeners.add(new RestEngineSchemaListener()); scbEngine.setBootListeners(listeners); scbEngine .addProducerMeta("sid1", new RestOperationMetaSchema()) .run(); swagger = Mockito.spy(scbEngine.getProducerMicroserviceMeta().ensureFindSchemaMeta("sid1").getSwagger()); } @AfterEach public void teardown() { scbEngine.destroy(); } private void findOperation(String operationId) { meta = Mockito.spy(scbEngine.getProducerMicroserviceMeta().operationMetas().get("test.sid1." + operationId)); operationMeta = Mockito.spy(RestMetaUtils.getRestOperationMeta(meta)); SchemaMeta schemaMeta = Mockito.spy(meta.getSchemaMeta()); Mockito.when(meta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getSwagger()).thenReturn(swagger); } @Test public void generatesAbsolutePathWithRootBasePath() { findOperation("textCharJsonChar"); MatcherAssert.assertThat(operationMeta.getAbsolutePath(), is("/textCharJsonChar")); } @Test public void generatesAbsolutePathWithNonRootBasePath() { findOperation("textCharJsonChar"); Server server = Mockito.mock(Server.class); Mockito.when(server.getUrl()).thenReturn("/rest"); Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/rest/textCharJsonChar")); } @Test public void generatesAbsolutePathWithNullPath() { findOperation("textCharJsonChar"); Server server = Mockito.mock(Server.class); Mockito.when(server.getUrl()).thenReturn(null); Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); Mockito.when(meta.getOperationPath()).thenReturn(null); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/")); } @Test public void generatesAbsolutePathWithEmptyPath() { findOperation("textCharJsonChar"); Server server = Mockito.mock(Server.class); Mockito.when(server.getUrl()).thenReturn(""); Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); Mockito.when(meta.getOperationPath()).thenReturn(""); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/")); } @Test public void consecutiveSlashesAreRemoved() { findOperation("textCharJsonChar"); Server server = Mockito.mock(Server.class); Mockito.when(server.getUrl()).thenReturn("//rest//"); Mockito.when(swagger.getServers()).thenReturn(Arrays.asList(server)); Mockito.when(meta.getOperationPath()).thenReturn("//sayHi//"); RestOperationMeta restOperationMeta = new RestOperationMeta(); restOperationMeta.init(meta); MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/rest/sayHi/")); } @Test public void testFormDataFlagTrue() { findOperation("form"); MatcherAssert.assertThat(operationMeta.isFormData(), is(true)); } @Test public void testFormDataFlagFalse() { findOperation("json"); MatcherAssert.assertThat(operationMeta.isFormData(), is(false)); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class PathVarParamWriterTest { @Test public void writePlainPath() throws Exception { PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); Map parameters = new HashMap<>(); parameters.put("test", "abc"); pathVarParamWriter.write(pathBuilder, parameters); Assertions.assertEquals("abc", pathBuilder.build()); } @Test public void writePathWithSpace() throws Exception { PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); Map parameters = new HashMap<>(); parameters.put("test", "a 20bc"); pathVarParamWriter.write(pathBuilder, parameters); Assertions.assertEquals("a%2020bc", pathBuilder.build()); } @Test public void writePathWithPercentage() throws Exception { PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); pathBuilder.appendPath("/api/"); Map parameters = new HashMap<>(); parameters.put("test", "a%%bc"); pathVarParamWriter.write(pathBuilder, parameters); Assertions.assertEquals("/api/a%25%25bc", pathBuilder.build()); } @Test public void writePathParamWithSlash() throws Exception { PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); pathBuilder.appendPath("/api/"); Map parameters = new HashMap<>(); parameters.put("test", "a/bc"); pathVarParamWriter.write(pathBuilder, parameters); Assertions.assertEquals("/api/a%2Fbc", pathBuilder.build()); } @Test public void writeIntegerParam() throws Exception { PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); Map parameters = new HashMap<>(); parameters.put("test", 12); pathVarParamWriter.write(pathBuilder, parameters); Assertions.assertEquals("12", pathBuilder.build()); } private PathVarParamWriter createPathVarParamWriter() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(restParam.getParamName()).thenReturn("test"); return new PathVarParamWriter(restParam); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; import io.swagger.v3.oas.models.parameters.QueryParameter; public class QueryVarParamWriterTest { private static QueryVarParamWriter queryVarParamWriterCsv; private static QueryVarParamWriter queryVarParamWriterMulti; private static QueryVarParamWriter queryVarParamWriterDefault; static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); QueryParameter parameter = new QueryParameter(); parameter.setName("q"); parameter.setSchema(new Schema()); parameter.setStyle(StyleEnum.FORM); parameter.setExplode(false); queryVarParamWriterCsv = new QueryVarParamWriter( new RestParam(null, parameter, String[].class)); parameter = new QueryParameter(); parameter.setName("q"); parameter.setSchema(new Schema()); parameter.setStyle(StyleEnum.FORM); parameter.setExplode(true); queryVarParamWriterMulti = new QueryVarParamWriter( new RestParam(null, parameter, String[].class)); parameter = new QueryParameter(); parameter.setName("q"); parameter.setSchema(new Schema()); queryVarParamWriterDefault = new QueryVarParamWriter( new RestParam(null, parameter, String[].class)); } @Test public void write() throws Exception { URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); Map parameters = new HashMap<>(); parameters.put("q", "a"); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=a", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=a", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=a", stringBuilder.build()); } @Test public void writeNull() throws Exception { URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); Map parameters = new HashMap<>(); parameters.put("test", null); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); } @Test public void writeArray() throws Exception { URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); Map parameters = new HashMap<>(); parameters.put("q", new String[] {"ab", "cd", "ef"}); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); // encode space char stringBuilder = new URLPathStringBuilder(); parameters.put("q", new String[] {"a b", " ", "", "ef"}); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); // pass blank string stringBuilder = new URLPathStringBuilder(); parameters.put("q", new String[] {""}); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=", stringBuilder.build()); // pass empty stringBuilder = new URLPathStringBuilder(); parameters.put("q", new String[] {}); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); // pass null parameters.put("q", new String[] {null}); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); parameters.put("q", new String[] {null, "ab", null, "cd", null, null, "", null, "ef", null}); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); } @Test public void writeList() throws Exception { List queryList = Arrays.asList("ab", "cd", "ef"); Map parameters = new HashMap<>(); parameters.put("q", queryList); URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); // encode space char parameters.put("q", Arrays.asList("a b", " ", "", "ef")); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); // pass blank string stringBuilder = new URLPathStringBuilder(); parameters.put("q", Collections.singletonList("")); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=", stringBuilder.build()); // pass empty stringBuilder = new URLPathStringBuilder(); parameters.put("q", new ArrayList<>()); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); // pass null parameters.put("q", Collections.singletonList(null)); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("", stringBuilder.build()); parameters.put("q", Arrays.asList(null, "ab", null, "cd", null, null, "", null, "ef", null)); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterCsv.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterMulti.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); stringBuilder = new URLPathStringBuilder(); queryVarParamWriterDefault.write(stringBuilder, parameters); Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import java.lang.reflect.Type; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.PathParameter; import io.swagger.v3.oas.models.parameters.QueryParameter; public class URLPathBuilderTest { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void beforeClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.emptyAsNull", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.query.ignoreRequiredCheck", boolean.class, false)) .thenReturn(false); } @Test public void testNormal() throws Exception { Map paramMap = new LinkedHashMap<>(); addParam("p0", int.class, PathParameter::new, paramMap); addParam("p1", String.class, PathParameter::new, paramMap); addParam("q0", int.class, QueryParameter::new, paramMap); addParam("q1", String.class, QueryParameter::new, paramMap); URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p0}/and/{p1}", paramMap); Map parameters = new HashMap<>(); parameters.put("p0", 10); parameters.put("p1", "abcPath"); parameters.put("q0", 11); parameters.put("q1", "queryABC"); Assertions.assertEquals("/path/10/and/abcPath?q0=11&q1=queryABC", urlPathBuilder.createRequestPath(parameters)); Assertions.assertEquals("/path/10/and/abcPath", urlPathBuilder.createPathString(parameters)); } @Test public void testEncode() throws Exception { Map paramMap = new LinkedHashMap<>(); addParam("p", String.class, PathParameter::new, paramMap); addParam("q", String.class, QueryParameter::new, paramMap); URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p}", paramMap); Map parameters = new HashMap<>(); parameters.put("p", "ab%% %cd%"); parameters.put("q", "ab%% %cd%"); Assertions.assertEquals("/path/ab%25%25%20%25cd%25?q=ab%25%25+%25cd%25", urlPathBuilder.createRequestPath(parameters)); Assertions.assertEquals("/path/ab%25%25%20%25cd%25", urlPathBuilder.createPathString(parameters)); } @Test public void testMultiQuery() throws Exception { Map paramMap = new LinkedHashMap<>(); addParam("strArr", String[].class, QueryParameter::new, paramMap); addParam("intArr", int[].class, QueryParameter::new, paramMap); URLPathBuilder urlPathBuilder = new URLPathBuilder("/path", paramMap); Map parameters = new HashMap<>(); parameters.put("strArr", new Object[] {"a", "b", "c"}); parameters.put("intArr", new Object[] {1, 2, 3}); Assertions.assertEquals("/path?strArr=a&strArr=b&strArr=c&intArr=1&intArr=2&intArr=3", urlPathBuilder.createRequestPath(parameters)); parameters.put("strArr", new Object[] {}); parameters.put("intArr", new Object[] {1, 2, 3}); Assertions.assertEquals("/path?intArr=1&intArr=2&intArr=3", urlPathBuilder.createRequestPath(parameters)); parameters.put("strArr", new Object[] {"a", "b", "c"}); parameters.put("intArr", new Object[] {}); Assertions.assertEquals("/path?strArr=a&strArr=b&strArr=c", urlPathBuilder.createRequestPath(parameters)); parameters.put("strArr", new Object[] {}); parameters.put("intArr", new Object[] {}); Assertions.assertEquals("/path", urlPathBuilder.createRequestPath(parameters)); } @Test public void testRegexPathParam() throws Exception { Map paramMap = new LinkedHashMap<>(); addParam("p0", int.class, PathParameter::new, paramMap); addParam("p1", String.class, PathParameter::new, paramMap); addParam("q0", int.class, QueryParameter::new, paramMap); addParam("q1", String.class, QueryParameter::new, paramMap); URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p0 : .*}/and/{p1:.*}", paramMap); Map parameters = new HashMap<>(); parameters.put("p0", 10); parameters.put("p1", "abcPath"); parameters.put("q0", 11); parameters.put("q1", "queryABC"); Assertions.assertEquals("/path/10/and/abcPath?q0=11&q1=queryABC", urlPathBuilder.createRequestPath(parameters)); Assertions.assertEquals("/path/10/and/abcPath", urlPathBuilder.createPathString(parameters)); } private void addParam(String paramName, Type paramType, ParameterConstructor constructor, Map paramMap) { Parameter parameter = constructor.construct(); parameter.setName(paramName); parameter.setSchema(new Schema()); paramMap.put(paramName, new RestParam(null, parameter, paramType)); } interface ParameterConstructor { Parameter construct(); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.definition.path; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class URLPathStringBuilderTest { @Test public void testNormal() { URLPathStringBuilder builder = new URLPathStringBuilder(); builder.appendPath("/path"); builder.appendQuery("q", "abc"); Assertions.assertEquals("/path?q=abc", builder.build()); } @Test public void appendPath() { URLPathStringBuilder builder = new URLPathStringBuilder(); builder.appendPath("/abc"); Assertions.assertEquals("/abc", builder.build()); builder.appendPath("/de fg"); Assertions.assertEquals("/abc/de fg", builder.build()); } @Test public void appendQuery() { URLPathStringBuilder builder = new URLPathStringBuilder(); Assertions.assertEquals("", builder.build()); builder.appendQuery("ab", "cd"); Assertions.assertEquals("?ab=cd", builder.build()); builder.appendQuery("ef", ""); Assertions.assertEquals("?ab=cd&ef=", builder.build()); builder.appendQuery("gh", "jk"); Assertions.assertEquals("?ab=cd&ef=&gh=jk", builder.build()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.filter.inner; import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import java.io.ByteArrayInputStream; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.SCBStatus; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.StringSchema; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.vertx.core.MultiMap; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.MediaType; public class RestServerCodecFilterTest { final RestServerCodecFilter codecFilter = new RestServerCodecFilter(); Invocation invocation; final Endpoint endpoint = Mockito.mock(Endpoint.class); final OperationMeta operationMeta = Mockito.mock(OperationMeta.class); final SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); final MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); final InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); final RestOperationMeta restOperationMeta = Mockito.mock(RestOperationMeta.class); final HttpTransportContext transportContext = Mockito.mock(HttpTransportContext.class); final HttpServletResponseEx responseEx = Mockito.mock(HttpServletResponseEx.class); final MultiMap headers = MultiMap.caseInsensitiveMultiMap(); final Part part = new InputStreamPart("ok", new ByteArrayInputStream(new byte[] {1, 2})); final FilterNode nextNode = new FilterNode(new AbstractFilter() { @Override public String getName() { return "f2"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Response response = Response.ok(part); response.setHeaders(headers); return CompletableFuture.completedFuture(response); } }); static SCBEngine engine; @BeforeAll public static void beforeClass() { Environment environment = Mockito.mock(Environment.class); engine = SCBBootstrap.createSCBEngineForTest(environment); engine.setEnvironment(environment); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); engine.setStatus(SCBStatus.UP); } @AfterAll public static void afterClass() { engine.destroy(); } @BeforeEach public void setUp() { Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); invocation = Mockito.spy(InvocationFactory.forProvider(endpoint, operationMeta, null)); Mockito.when(responseEx.sendPart(part)).thenReturn(CompletableFuture.completedFuture(null)); } private void mockDecodeRequestFail() { Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); Mockito.when(transportContext.getResponseEx()).thenReturn(responseEx); Mockito.when(invocation.getRequestEx()) .thenThrow(new RuntimeExceptionWithoutStackTrace("mock encode request failed")); } @Test public void should_not_invoke_filter_when_decode_request_failed() { FilterNode nextNode = Mockito.mock(FilterNode.class); mockDecodeRequestFail(); codecFilter.onFilter(invocation, nextNode); Mockito.verify(nextNode, Mockito.times(0)).onFilter(invocation); } @Test public void should_convert_exception_to_response_when_decode_request_failed() throws ExecutionException, InterruptedException { mockDecodeRequestFail(); Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())) .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); Assertions.assertThrows(ExecutionException.class, () -> codecFilter.onFilter(invocation, nextNode).get()); } private void success_invocation() throws InterruptedException, ExecutionException { Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); HttpServletRequestEx requestExt = Mockito.mock(HttpServletRequestEx.class); Mockito.when(invocation.getRequestEx()).thenReturn(requestExt); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Operation swaggerOperation = Mockito.mock(Operation.class); Mockito.when(operationMeta.getSwaggerOperation()).thenReturn(swaggerOperation); ApiResponses apiResponses = Mockito.mock(ApiResponses.class); Mockito.when(swaggerOperation.getResponses()).thenReturn(apiResponses); ApiResponse apiResponse = Mockito.mock(ApiResponse.class); Mockito.when(apiResponses.get("200")).thenReturn(apiResponse); Content content = new Content(); content.addMediaType(MediaType.APPLICATION_JSON, new io.swagger.v3.oas.models.media.MediaType()); content.get(MediaType.APPLICATION_JSON).setSchema(new StringSchema()); Mockito.when(apiResponse.getContent()).thenReturn(content); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())) .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); JavaType javaType = Mockito.mock(JavaType.class); Mockito.when(invocationRuntimeType.findResponseType(200)).thenReturn(javaType); Mockito.when(javaType.getRawClass()).thenAnswer(invocationOnMock -> Part.class); Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); Mockito.when(transportContext.getResponseEx()).thenReturn(responseEx); codecFilter.onFilter(invocation, nextNode).get(); } @Test public void should_encode_response_header() throws ExecutionException, InterruptedException { headers.add("h1", "v1"); success_invocation(); Mockito.verify(responseEx).addHeader("h1", "v1"); } @Test public void should_not_encode_content_length_header() throws ExecutionException, InterruptedException { headers.add("h1", "v1") .add("h2", "v2") .add(CONTENT_LENGTH, "10"); success_invocation(); Mockito.verify(responseEx, Mockito.times(1)).addHeader("h1", "v1"); Mockito.verify(responseEx, Mockito.times(1)).addHeader("h2", "v2"); } @Test public void should_not_encode_transfer_encoding_header() throws ExecutionException, InterruptedException { headers.add("h1", "v1") .add("h2", "v2") .add(TRANSFER_ENCODING, "test"); success_invocation(); Mockito.verify(responseEx, Mockito.times(1)).addHeader("h1", "v1"); Mockito.verify(responseEx, Mockito.times(1)).addHeader("h2", "v2"); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestMicroservicePaths.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.common.rest.RestEngineSchemaListener; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestMicroservicePaths { SCBEngine scbEngine; MicroservicePaths paths; @BeforeEach public void setup() { Environment environment = Mockito.mock(Environment.class); scbEngine = SCBBootstrap.createSCBEngineForTest(environment); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); List listeners = new ArrayList<>(); listeners.add(new RestEngineSchemaListener()); ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); scbEngine.setTransportManager(transportManager); scbEngine.setExecutorManager(executorManager); scbEngine.setBootListeners(listeners); scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); ServicePathManager spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); paths = spm.producerPaths; } @AfterEach public void teardown() { scbEngine.destroy(); } @Test public void staticGroup() { RestOperationMeta meta = paths.getStaticPathOperationMap().get("/static/").findValue("POST"); Assertions.assertSame("postStatic", meta.getOperationMeta().getOperationId()); meta = paths.getStaticPathOperationMap().get("/static/").findValue("GET"); Assertions.assertSame("getStatic", meta.getOperationMeta().getOperationId()); } @Test public void testAddResourceStaticDuplicatedHttpMethod() { RestOperationMeta staticResPost = Mockito.mock(RestOperationMeta.class); Mockito.when(staticResPost.getHttpMethod()).thenReturn("POST"); Mockito.when(staticResPost.getAbsolutePath()).thenReturn("/static/"); Mockito.when(staticResPost.isAbsoluteStaticPath()).thenReturn(true); ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, () -> paths.addResource(staticResPost)); Assertions.assertEquals("operation with url /static/, method POST is duplicated.", exception.getMessage()); } @Test public void dynamicPath() { Assertions.assertEquals("dynamicExId", paths.getDynamicPathOperationList().get(0).getOperationMeta().getOperationId()); Assertions.assertEquals("dynamicId", paths.getDynamicPathOperationList().get(1).getOperationMeta().getOperationId()); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestPathSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @Path("/") public class TestPathSchema { @Path("/static") @GET public void getStatic() { } @Path("/static") @POST public void postStatic() { } @Path("/staticEx") @GET public void getStaticEx() { } @Path("/dynamic/{id}") @GET public void dynamicId(@PathParam("id") String id) { } @Path("/dynamicEx/{id}") @GET public void dynamicExId(@PathParam("id") String id) { } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestServicePathManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.locator; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.common.rest.RestEngineSchemaListener; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestServicePathManager { SCBEngine scbEngine; Environment environment; @BeforeEach public void setUp() { environment = Mockito.mock(Environment.class); scbEngine = SCBBootstrap.createSCBEngineForTest(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); } @AfterEach public void tearDown() { scbEngine.destroy(); ClassLoaderScopeContext.clearClassLoaderScopeProperty(); } @Test public void testBuildProducerPathsNoPrefix() { ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); scbEngine.setTransportManager(transportManager); scbEngine.setExecutorManager(executorManager); List listeners = new ArrayList<>(); listeners.add(new RestEngineSchemaListener()); scbEngine.setBootListeners(listeners); scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); ServicePathManager spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); Assertions.assertSame(spm.producerPaths, spm.swaggerPaths); scbEngine.destroy(); } @Test public void testBuildProducerPathsHasPrefix() { ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, "/root/rest"); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty(DefinitionConst.REGISTER_URL_PREFIX, boolean.class, false)).thenReturn(false); ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); TransportManager transportManager = Mockito.mock(TransportManager.class); scbEngine.setTransportManager(transportManager); scbEngine.setExecutorManager(executorManager); List listeners = new ArrayList<>(); listeners.add(new RestEngineSchemaListener()); scbEngine.setBootListeners(listeners); scbEngine.addProducerMeta("sid1", new TestPathSchema()) .run(); ServicePathManager spm = ServicePathManager.getServicePathManager(scbEngine.getProducerMicroserviceMeta()); // all locate should be success spm.producerLocateOperation("/root/rest/static/", "GET"); spm.producerLocateOperation("/root/rest/static/", "POST"); spm.producerLocateOperation("/root/rest/dynamic/1/", "GET"); spm.producerLocateOperation("/root/rest/dynamicEx/1/", "GET"); scbEngine.destroy(); } } ================================================ FILE: common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.common.rest.resource; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response.Status; public class TestClassPathStaticResourceHandler { static ClassPathStaticResourceHandler handler = new ClassPathStaticResourceHandler(); @BeforeAll public static void setup() { handler.setWebRoot("web-root/"); } @Test public void normal() throws IOException { Response response = handler.handle("index.html"); Part part = response.getResult(); try (InputStream is = part.getInputStream()) { Assertions.assertTrue(IOUtils.toString(is, StandardCharsets.UTF_8).trim().endsWith("")); } Assertions.assertEquals("text/html", part.getContentType()); Assertions.assertEquals("text/html", response.getHeader(HttpHeaders.CONTENT_TYPE)); Assertions.assertEquals("inline", response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); } @Test public void notExist() { Response response = handler.handle("notExist.html"); InvocationException invocationException = response.getResult(); Assertions.assertEquals(Status.NOT_FOUND, invocationException.getStatus()); Assertions.assertEquals(Status.NOT_FOUND.getReasonPhrase(), ((CommonExceptionData) invocationException.getErrorData()).getMessage()); Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertEquals("Not Found", response.getReasonPhrase()); } @Test public void attack() { Response response = handler.handle("../microservice.yaml"); InvocationException invocationException = response.getResult(); Assertions.assertEquals(Status.NOT_FOUND, invocationException.getStatus()); Assertions.assertEquals(Status.NOT_FOUND.getReasonPhrase(), ((CommonExceptionData) invocationException.getErrorData()).getMessage()); Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertEquals("Not Found", response.getReasonPhrase()); } @Test public void readContentFailed() throws IOException { handler = Mockito.spy(TestClassPathStaticResourceHandler.handler); Mockito.when(handler.findResource("web-root/index.html")) .thenThrow(new RuntimeExceptionWithoutStackTrace("read content failed.")); try (LogCollector logCollector = new LogCollector()) { Response response = handler.handle("index.html"); Assertions.assertEquals("failed to process static resource, path=web-root/index.html", logCollector.getLastEvents().getMessage().getFormattedMessage()); InvocationException invocationException = response.getResult(); Assertions.assertEquals(Status.INTERNAL_SERVER_ERROR, invocationException.getStatus()); Assertions.assertEquals("failed to process static resource.", ((CommonExceptionData) invocationException.getErrorData()).getMessage()); Assertions.assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); Assertions.assertEquals(Status.INTERNAL_SERVER_ERROR.getReasonPhrase(), response.getReasonPhrase()); } } } ================================================ FILE: common/common-rest/src/test/resources/log4j2.xml ================================================ ================================================ FILE: common/common-rest/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- APPLICATION_ID: test service_description: name: test version: 0.0.1 servicecomb: service: registry: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 highway: address: 0.0.0.0:7070 ================================================ FILE: common/common-rest/src/test/resources/web-root/index.html ================================================ ================================================ FILE: common/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default common Java Chassis::Common pom common-protobuf common-rest common-access-log ================================================ FILE: core/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default java-chassis-core Java Chassis::Core org.apache.servicecomb foundation-vertx org.apache.servicecomb foundation-registry org.apache.servicecomb swagger-invocation-core org.apache.servicecomb swagger-generator-core io.zipkin.brave brave org.hibernate.validator hibernate-validator org.apache.servicecomb servicecomb-governance org.springframework.boot spring-boot-autoconfigure io.vertx vertx-codegen provided org.apache.servicecomb foundation-test-scaffolding test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/BootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.springframework.core.Ordered; public interface BootListener extends Ordered { enum EventType { BEFORE_FILTER, AFTER_FILTER, BEFORE_PRODUCER_PROVIDER, AFTER_PRODUCER_PROVIDER, BEFORE_CONSUMER_PROVIDER, AFTER_CONSUMER_PROVIDER, BEFORE_TRANSPORT, AFTER_TRANSPORT, BEFORE_REGISTRY, AFTER_REGISTRY, BEFORE_CLOSE, AFTER_CLOSE } class BootEvent { private SCBEngine scbEngine; private EventType eventType; public BootEvent() { } public BootEvent(SCBEngine scbEngine, EventType eventType) { this.scbEngine = scbEngine; this.eventType = eventType; } public SCBEngine getScbEngine() { return scbEngine; } public BootEvent setScbEngine(SCBEngine scbEngine) { this.scbEngine = scbEngine; return this; } public EventType getEventType() { return eventType; } public BootEvent setEventType(EventType eventType) { this.eventType = eventType; return this; } } @Override default int getOrder() { return 0; } default void onBootEvent(BootEvent event) { switch (event.eventType) { case BEFORE_FILTER: onBeforeFilter(event); return; case AFTER_FILTER: onAfterFilter(event); return; case BEFORE_PRODUCER_PROVIDER: onBeforeProducerProvider(event); return; case AFTER_PRODUCER_PROVIDER: onAfterProducerProvider(event); return; case BEFORE_CONSUMER_PROVIDER: onBeforeConsumerProvider(event); return; case AFTER_CONSUMER_PROVIDER: onAfterConsumerProvider(event); return; case BEFORE_TRANSPORT: onBeforeTransport(event); return; case AFTER_TRANSPORT: onAfterTransport(event); return; case BEFORE_REGISTRY: onBeforeRegistry(event); return; case AFTER_REGISTRY: onAfterRegistry(event); return; case BEFORE_CLOSE: onBeforeClose(event); return; case AFTER_CLOSE: onAfterClose(event); return; default: throw new IllegalStateException("unknown boot event type: " + event.eventType); } } default void onBeforeFilter(BootEvent event) { } default void onAfterFilter(BootEvent event) { } default void onBeforeProducerProvider(BootEvent event) { } default void onAfterProducerProvider(BootEvent event) { } default void onBeforeConsumerProvider(BootEvent event) { } default void onAfterConsumerProvider(BootEvent event) { } default void onBeforeTransport(BootEvent event) { } default void onAfterTransport(BootEvent event) { } default void onBeforeRegistry(BootEvent event) { } default void onAfterRegistry(BootEvent event) { } default void onBeforeClose(BootEvent event) { } default void onAfterClose(BootEvent event) { } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/CoreConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.apache.servicecomb.registry.definition.DefinitionConst; public final class CoreConst { private CoreConst() { } public static final String CSE_CONTEXT = "x-cse-context"; public static final String TRANSPORT_NAME = "x-transport-name"; public static final String RESTFUL = "rest"; public static final String HIGHWAY = "highway"; public static final String WEBSOCKET = "websocket"; public static final String ANY_TRANSPORT = ""; public static final String VERSION_RULE_LATEST = DefinitionConst.VERSION_RULE_LATEST; public static final String DEFAULT_VERSION_RULE = DefinitionConst.VERSION_RULE_ALL; public static final String PRODUCER_OPERATION = "producer-operation"; public static final String CONSUMER_OPERATION = "consumer-operation"; public static final String SRC_MICROSERVICE = "x-cse-src-microservice"; public static final String SRC_SERVICE_ID = "x-src-serviceId"; public static final String SRC_INSTANCE_ID = "x-src-instanceId"; public static final String TARGET_MICROSERVICE = "x-cse-target-microservice"; public static final String REMOTE_ADDRESS = "x-cse-remote-address"; public static final String AUTH_TOKEN = "x-cse-auth-rsatoken"; public static final String TRACE_ID_NAME = "X-B3-TraceId"; // controlling whether to print stack information with sensitive errors public static final String PRINT_SENSITIVE_ERROR_MESSAGE = "servicecomb.error.printSensitiveErrorMessage"; public static final String SWAGGER_EXPORT_ENABLED = "servicecomb.swagger.export.enabled"; public static final String SWAGGER_DIRECTORY = "servicecomb.swagger.export.directory"; } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/Endpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; /** * Internal representation for microservice instance address. */ public class Endpoint { // Registry address: http://192.168.1.1:8080 // see: http://www.ietf.org/rfc/rfc2396.txt private final String endpoint; private final Transport transport; private final DiscoveryInstance instance; // Internal address format recognized by Transport private final Object address; public Endpoint(Transport transport, String endpoint) { this(transport, endpoint, null); } public Endpoint(Transport transport, String endpoint, DiscoveryInstance instance) { this.transport = transport; this.endpoint = endpoint; this.instance = instance; this.address = transport.parseAddress(this.endpoint); } public Endpoint(Transport transport, String endpoint, StatefulDiscoveryInstance instance, Object address) { this.transport = transport; this.endpoint = endpoint; this.instance = instance; this.address = address; } public String getEndpoint() { return endpoint; } public DiscoveryInstance getMicroserviceInstance() { return instance; } public Transport getTransport() { return transport; } public Object getAddress() { return address; } @Override public String toString() { return endpoint; } @Override public boolean equals(Object o) { if (o instanceof Endpoint) { return this.endpoint.equals(((Endpoint) o).getEndpoint()); } return false; } @Override public int hashCode() { return this.endpoint.hashCode(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/Invocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationEncodeResponseStartEvent; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.core.tracing.TraceIdGenerator; import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.fasterxml.jackson.databind.JavaType; public class Invocation extends SwaggerInvocation { private static final Collection TRACE_ID_GENERATORS = loadTraceIdGenerators(); protected static final AtomicLong INVOCATION_ID = new AtomicLong(); static Collection loadTraceIdGenerators() { return SPIServiceUtils.getPriorityHighestServices(TraceIdGenerator::getName, TraceIdGenerator.class); } protected ReferenceConfig referenceConfig; private InvocationRuntimeType invocationRuntimeType; // 本次调用对应的schemaMeta private SchemaMeta schemaMeta; // 本次调用对应的operationMeta private OperationMeta operationMeta; // loadbalance查询得到的地址,由transport client使用 // 之所以不放在handlerContext中,是因为这属于核心数据,没必要走那样的机制 private Endpoint endpoint; // 只用于handler之间传递数据,是本地数据 private final Map handlerContext = localContext; // 应答的处理器 // 同步模式:避免应答在网络线程中处理解码等等业务级逻辑 private Executor responseExecutor; private volatile boolean sync = true; private final InvocationStageTrace invocationStageTrace = new InvocationStageTrace(this); private HttpServletRequestEx requestEx; private volatile boolean finished; private volatile long invocationId; private TraceIdLogger traceIdLogger; private Map invocationArguments = Collections.emptyMap(); private Object[] producerArguments; private Map swaggerArguments = Collections.emptyMap(); public Invocation() { // An empty invocation, used to mock or some other scenario do not need operation information. traceIdLogger = new TraceIdLogger(this); } public Invocation(ReferenceConfig referenceConfig, OperationMeta operationMeta, InvocationRuntimeType invocationRuntimeType, Map swaggerArguments) { this.invocationType = InvocationType.CONSUMER; this.referenceConfig = referenceConfig; this.invocationRuntimeType = invocationRuntimeType; init(operationMeta, swaggerArguments); } public Invocation(Endpoint endpoint, OperationMeta operationMeta, Map swaggerArguments) { this.invocationType = InvocationType.PROVIDER; this.invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); this.endpoint = endpoint; init(operationMeta, swaggerArguments); } private void init(OperationMeta operationMeta, Map swaggerArguments) { this.invocationId = INVOCATION_ID.getAndIncrement(); this.schemaMeta = operationMeta.getSchemaMeta(); this.operationMeta = operationMeta; this.setSwaggerArguments(swaggerArguments); traceIdLogger = new TraceIdLogger(this); } public String getTransportName() { if (endpoint == null || endpoint.getTransport() == null) { return null; } return endpoint.getTransport().getName(); } public Transport getTransport() { if (endpoint == null) { throw new IllegalStateException( "Endpoint is empty. Forget to configure \"loadbalance\" in consumer handler chain?"); } return endpoint.getTransport(); } public Executor getResponseExecutor() { return responseExecutor; } public void setResponseExecutor(Executor responseExecutor) { this.responseExecutor = responseExecutor; } public SchemaMeta getSchemaMeta() { return schemaMeta; } public OperationMeta getOperationMeta() { return operationMeta; } public Map getInvocationArguments() { return this.invocationArguments; } public Map getSwaggerArguments() { return this.swaggerArguments; } public Object getInvocationArgument(String name) { return this.invocationArguments.get(name); } public Object getSwaggerArgument(String name) { return this.swaggerArguments.get(name); } public void setInvocationArguments(Map invocationArguments) { if (invocationArguments == null) { // Empty arguments this.invocationArguments = new HashMap<>(0); return; } this.invocationArguments = invocationArguments; buildSwaggerArguments(); } private void buildSwaggerArguments() { if (!this.invocationRuntimeType.isRawConsumer()) { this.swaggerArguments = this.invocationRuntimeType.getArgumentsMapper() .invocationArgumentToSwaggerArguments(this, this.invocationArguments); } else { this.swaggerArguments = invocationArguments; } } public void setSwaggerArguments(Map swaggerArguments) { if (swaggerArguments == null) { // Empty arguments this.swaggerArguments = new HashMap<>(0); return; } this.swaggerArguments = swaggerArguments; buildInvocationArguments(); } private void buildInvocationArguments() { if (operationMeta.getSwaggerProducerOperation() != null && !isEdge()) { this.invocationArguments = operationMeta.getSwaggerProducerOperation().getArgumentsMapper() .swaggerArgumentToInvocationArguments(this, swaggerArguments); } else { this.invocationArguments = swaggerArguments; } } public Object[] toProducerArguments() { if (producerArguments != null) { return producerArguments; } Method method = operationMeta.getSwaggerProducerOperation().getProducerMethod(); Object[] args = new Object[method.getParameterCount()]; for (int i = 0; i < method.getParameterCount(); i++) { args[i] = this.invocationArguments.get(method.getParameters()[i].getName()); } return producerArguments = args; } public Endpoint getEndpoint() { return endpoint; } public void setEndpoint(Endpoint endpoint) { this.endpoint = endpoint; } public Map getHandlerContext() { return handlerContext; } public String getSchemaId() { return schemaMeta.getSchemaId(); } public String getOperationName() { return operationMeta.getOperationId(); } public String getProviderTransportName() { if (operationMeta.getSwaggerOperation().getExtensions() != null && operationMeta.getSwaggerOperation().getExtensions().get(CoreConst.TRANSPORT_NAME) != null) { return (String) operationMeta.getSwaggerOperation().getExtensions().get(CoreConst.TRANSPORT_NAME); } if (schemaMeta.getSwagger().getExtensions() != null && schemaMeta.getSwagger().getExtensions().get(CoreConst.TRANSPORT_NAME) != null) { return (String) schemaMeta.getSwagger().getExtensions().get(CoreConst.TRANSPORT_NAME); } return null; } public String getConfigTransportName() { if (getProviderTransportName() != null) { return getProviderTransportName(); } return referenceConfig.getTransport(); } public String getRealTransportName() { return (endpoint != null) ? endpoint.getTransport().getName() : getConfigTransportName(); } public String getMicroserviceName() { return schemaMeta.getMicroserviceName(); } public String getAppId() { return schemaMeta.getMicroserviceMeta().getAppId(); } public MicroserviceMeta getMicroserviceMeta() { return schemaMeta.getMicroserviceMeta(); } public InvocationRuntimeType getInvocationRuntimeType() { return this.invocationRuntimeType; } public JavaType findResponseType(int statusCode) { return this.invocationRuntimeType.findResponseType(statusCode); } public void setSuccessResponseType(JavaType javaType) { this.invocationRuntimeType.setSuccessResponseType(javaType); } @Override public String getInvocationQualifiedName() { return invocationType.name() + " " + getRealTransportName() + " " + getOperationMeta().getMicroserviceQualifiedName(); } public String getMicroserviceQualifiedName() { return operationMeta.getMicroserviceQualifiedName(); } protected void initTraceId() { for (TraceIdGenerator traceIdGenerator : TRACE_ID_GENERATORS) { initTraceId(traceIdGenerator); } } protected void initTraceId(TraceIdGenerator traceIdGenerator) { if (!StringUtils.isEmpty(getTraceId(traceIdGenerator.getTraceIdKeyName()))) { // if invocation context contains traceId, nothing needed to do return; } if (requestEx == null) { // it's a new consumer invocation, must generate a traceId addContext(traceIdGenerator.getTraceIdKeyName(), traceIdGenerator.generate()); return; } String traceId = requestEx.getHeader(traceIdGenerator.getTraceIdKeyName()); if (!StringUtils.isEmpty(traceId)) { // if request header contains traceId, save traceId into invocation context addContext(traceIdGenerator.getTraceIdKeyName(), traceId); return; } // if traceId not found, generate a traceId addContext(traceIdGenerator.getTraceIdKeyName(), traceIdGenerator.generate()); } public void onStart() { initTraceId(); EventManager.post(new InvocationStartEvent(this)); } public void onStart(HttpServletRequestEx requestEx) { this.requestEx = requestEx; onStart(); } public void onStartSendRequest() { EventManager.post(new InvocationStartSendRequestEvent(this)); } public void onBusinessMethodStart() { invocationStageTrace.startBusinessExecute(); EventManager.post(new InvocationBusinessMethodStartEvent(this)); } public void onEncodeResponseStart(Response response) { invocationStageTrace.startProviderEncodeResponse(); EventManager.post(new InvocationEncodeResponseStartEvent(this, response)); } public void onEncodeResponseFinish() { invocationStageTrace.finishProviderEncodeResponse(); } public void onBusinessFinish() { invocationStageTrace.finishBusinessExecute(); EventManager.post(new InvocationBusinessFinishEvent(this)); } public void onFinish(Response response) { if (finished) { // avoid to post repeated event return; } finished = true; invocationStageTrace.finish(); EventManager.post(new InvocationFinishEvent(this, response)); } // for retry, reset invocation and try it again public void reset() { finished = false; } public boolean isFinished() { return finished; } public boolean isSync() { return sync; } public void setSync(boolean sync) { this.sync = sync; } public boolean isConsumer() { return InvocationType.CONSUMER.equals(invocationType); } public boolean isProducer() { return InvocationType.PROVIDER.equals(invocationType); } public boolean isEdge() { return InvocationType.EDGE.equals(invocationType); } public void setEdge() { this.invocationType = InvocationType.EDGE; } public long getInvocationId() { return invocationId; } public TraceIdLogger getTraceIdLogger() { return this.traceIdLogger; } public HttpServletRequestEx getRequestEx() { return requestEx; } public InvocationStageTrace getInvocationStageTrace() { return invocationStageTrace; } public String getTraceId() { return getContext(CoreConst.TRACE_ID_NAME); } public String getTraceId(String traceIdName) { return getContext(traceIdName); } // ensure sync consumer invocation response flow not run in eventLoop public CompletableFuture optimizeSyncConsumerThread(CompletableFuture future) { if (sync && !InvokerUtils.isInEventLoop()) { return AsyncUtils.tryCatchSupplier(() -> InvokerUtils.toSync(future, getWaitTime())); } return future; } public long getWaitTime() { if (getOperationMeta().getConfig().getMsInvocationTimeout() > 0) { // if invocation timeout configured, use it. return getOperationMeta().getConfig().getMsInvocationTimeout(); } // In invocation handlers, may call other microservices, invocation // timeout may be much longer than request timeout. // But this is quite rare, for simplicity, default two times of request timeout. // If users need longer timeout, can configure invocation timeout. return getOperationMeta().getConfig().getMsRequestTimeout() * 2; } /** * Check if invocation is timeout. * * NOTICE: this method only trigger event to ask the target checker to do the real check. So this method * will only take effect when timeout checker is enabled. * * e.g. InvocationTimeoutBootListener.ENABLE_TIMEOUT_CHECK is enabled. * * @throws InvocationException if timeout, throw an exception. Will not throw exception twice if this method called * after timeout. */ public void ensureInvocationNotTimeout() throws InvocationException { EventManager.post(new InvocationTimeoutCheckEvent(this)); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; public class NonSwaggerInvocation extends Invocation { private final String appId; private final String microserviceName; public NonSwaggerInvocation(String appId, String microserviceName) { this.appId = appId; this.microserviceName = microserviceName; } @Override public String getSchemaId() { return "third-schema"; } @Override public String getOperationName() { return "third-operation"; } @Override public String getInvocationQualifiedName() { return microserviceName; } @Override public String getConfigTransportName() { return CoreConst.RESTFUL; } @Override public String getMicroserviceName() { return microserviceName; } @Override public String getAppId() { return appId; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/ProducerProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.util.List; import org.apache.servicecomb.core.provider.producer.ProducerMeta; public interface ProducerProvider { List init(); String getName(); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.core.filter.FilterChainsManager; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.EnvironmentAware; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; public class SCBApplicationListener implements ApplicationListener, Ordered, ApplicationContextAware, EnvironmentAware { private Class initEventClass = ContextRefreshedEvent.class; private ApplicationContext applicationContext; private final SCBEngine scbEngine; public SCBApplicationListener(SCBEngine scbEngine) { this.scbEngine = scbEngine; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (this.applicationContext == applicationContext) { // same object. avoid initialize many times. return; } this.applicationContext = applicationContext; BeanUtils.setContext(applicationContext); HttpClients.load(); } @Override public void setEnvironment(Environment environment) { scbEngine.init(); } public void setInitEventClass(Class initEventClass) { this.initEventClass = initEventClass; } @Override public int getOrder() { // should run before default listener, eg: ZuulConfiguration return -1000; } @Override public void onApplicationEvent(ApplicationEvent event) { if (initEventClass.isInstance(event)) { if (applicationContext instanceof AbstractApplicationContext) { ((AbstractApplicationContext) applicationContext).registerShutdownHook(); } scbEngine.setPriorityPropertyManager(applicationContext.getBean(PriorityPropertyManager.class)); scbEngine.setFilterChainsManager(applicationContext.getBean(FilterChainsManager.class)); scbEngine.getProducerProviderManager().getProducerProviderList() .addAll(applicationContext.getBeansOfType(ProducerProvider.class).values()); scbEngine.run(); } else if (event instanceof ContextClosedEvent) { if (SCBEngine.getInstance() != null) { SCBEngine.getInstance().destroy(); } } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/SCBEngine.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.core.BootListener.BootEvent; import org.apache.servicecomb.core.BootListener.EventType; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.filter.FilterChainsManager; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; import org.apache.servicecomb.core.provider.consumer.ReferenceConfigManager; import org.apache.servicecomb.core.provider.producer.ProducerProviderManager; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import jakarta.ws.rs.core.Response.Status; public class SCBEngine { private static final Logger LOGGER = LoggerFactory.getLogger(SCBEngine.class); public static final String CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC = "servicecomb.boot.turnDown.waitInSeconds"; public static final long DEFAULT_TURN_DOWN_STATUS_WAIT_SEC = 0; // TODO: will remove in future. Too many codes need refactor. private static volatile SCBEngine INSTANCE; private ApplicationContext applicationContext; private FilterChainsManager filterChainsManager; private ProducerProviderManager producerProviderManager; private ConsumerProviderManager consumerProviderManager; private MicroserviceMeta producerMicroserviceMeta; private TransportManager transportManager; private List bootListeners; private final AtomicLong invocationStartedCounter = new AtomicLong(); private final AtomicLong invocationFinishedCounter = new AtomicLong(); private volatile SCBStatus status = SCBStatus.DOWN; private final EventBus eventBus; private ExecutorManager executorManager; private PriorityPropertyManager priorityPropertyManager; private List bootUpInformationCollectors; private final SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); private OpenAPIRegistryManager openAPIRegistryManager; private RegistrationManager registrationManager; private DiscoveryManager discoveryManager; private Environment environment; private ReferenceConfigManager referenceConfigManager; public SCBEngine() { eventBus = EventManager.getEventBus(); eventBus.register(this); INSTANCE = this; producerProviderManager = new ProducerProviderManager(this); } public static SCBEngine getInstance() { if (INSTANCE == null) { throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("SCBEngine is not initialized yet.")); } return INSTANCE; } @Autowired public void setReferenceConfigManager(ReferenceConfigManager referenceConfigManager) { this.referenceConfigManager = referenceConfigManager; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } public Environment getEnvironment() { return this.environment; } @Autowired @SuppressWarnings("unused") public void setBootUpInformationCollectors(List bootUpInformationCollectors) { this.bootUpInformationCollectors = bootUpInformationCollectors; } @Autowired @SuppressWarnings("unused") public void setBootListeners(List listeners) { this.bootListeners = listeners; } @Autowired public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; } @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Autowired public void setOpenAPIRegistryManager(OpenAPIRegistryManager openAPIRegistryManager) { this.openAPIRegistryManager = openAPIRegistryManager; } @Autowired public void setConsumerProviderManager(ConsumerProviderManager consumerProviderManager) { this.consumerProviderManager = consumerProviderManager; } @Autowired public void setExecutorManager(ExecutorManager executorManager) { this.executorManager = executorManager; } @Autowired public void setTransportManager(TransportManager transportManager) { this.transportManager = transportManager; } public RegistrationManager getRegistrationManager() { return this.registrationManager; } public ApplicationContext getApplicationContext() { return applicationContext; } public String getAppId() { return BootStrapProperties.readApplication(environment); } public void setStatus(SCBStatus status) { this.status = status; } public SCBStatus getStatus() { return status; } public OpenAPIRegistryManager getOpenAPIRegistryManager() { return this.openAPIRegistryManager; } public FilterChainsManager getFilterChainsManager() { return filterChainsManager; } public SCBEngine setFilterChainsManager(FilterChainsManager filterChainsManager) { this.filterChainsManager = filterChainsManager; return this; } public PriorityPropertyManager getPriorityPropertyManager() { return priorityPropertyManager; } public SCBEngine setPriorityPropertyManager(PriorityPropertyManager priorityPropertyManager) { this.priorityPropertyManager = priorityPropertyManager; return this; } public EventBus getEventBus() { return eventBus; } public ExecutorManager getExecutorManager() { return executorManager; } public ProducerProviderManager getProducerProviderManager() { return producerProviderManager; } public void setProducerProviderManager(ProducerProviderManager producerProviderManager) { this.producerProviderManager = producerProviderManager; } public ConsumerProviderManager getConsumerProviderManager() { return consumerProviderManager; } public TransportManager getTransportManager() { return transportManager; } public SwaggerEnvironment getSwaggerEnvironment() { return swaggerEnvironment; } public SCBEngine addProducerMeta(String schemaId, Object instance) { getProducerProviderManager().addProducerMeta(schemaId, instance); return this; } protected void triggerEvent(EventType eventType) { BootEvent event = new BootEvent(); event.setScbEngine(this); event.setEventType(eventType); for (BootListener listener : bootListeners) { listener.onBootEvent(event); } } protected void safeTriggerEvent(EventType eventType) { BootEvent event = new BootEvent(); event.setScbEngine(this); event.setEventType(eventType); for (BootListener listener : bootListeners) { try { listener.onBootEvent(event); LOGGER.info("BootListener {} succeed to process {}.", listener.getClass().getName(), eventType); } catch (Throwable e) { LOGGER.error("BootListener {} failed to process {}.", listener.getClass().getName(), eventType, e); } } } @AllowConcurrentEvents @Subscribe @SuppressWarnings("unused") public void onInvocationStart(InvocationStartEvent event) { invocationStartedCounter.incrementAndGet(); } @AllowConcurrentEvents @Subscribe @SuppressWarnings("unused") public void onInvocationFinish(InvocationFinishEvent event) { invocationFinishedCounter.incrementAndGet(); } public synchronized SCBEngine init() { this.discoveryManager.init(); this.registrationManager.init(); return this; } public synchronized SCBEngine run() { if (SCBStatus.DOWN.equals(status)) { try { doRun(); printServiceInfo(); } catch (Throwable e) { LOGGER.error("Failed to start ServiceComb due to errors and close", e); try { destroy(); } catch (Exception exception) { LOGGER.info("destroy has some error.", exception); } status = SCBStatus.FAILED; throw new IllegalStateException("ServiceComb init failed.", e); } } return this; } private void printServiceInfo() { StringBuilder serviceInfo = new StringBuilder(); serviceInfo.append("Service information is shown below:\n"); for (BootUpInformationCollector bootUpInformationCollector : bootUpInformationCollectors) { String info = bootUpInformationCollector.collect(this); if (StringUtils.isEmpty(info)) { continue; } serviceInfo.append(info); if (!info.endsWith("\n")) { serviceInfo.append('\n'); } } LOGGER.info(serviceInfo.toString()); } private void doRun() throws Exception { status = SCBStatus.STARTING; triggerEvent(EventType.BEFORE_FILTER); filterChainsManager.init(); triggerEvent(EventType.AFTER_FILTER); createProducerMicroserviceMeta(); triggerEvent(EventType.BEFORE_PRODUCER_PROVIDER); producerProviderManager.init(); triggerEvent(EventType.AFTER_PRODUCER_PROVIDER); triggerEvent(EventType.BEFORE_CONSUMER_PROVIDER); consumerProviderManager.init(); triggerEvent(EventType.AFTER_CONSUMER_PROVIDER); triggerEvent(EventType.BEFORE_TRANSPORT); transportManager.init(this); triggerEvent(EventType.AFTER_TRANSPORT); triggerEvent(EventType.BEFORE_REGISTRY); registrationManager.run(); discoveryManager.run(); status = SCBStatus.UP; triggerEvent(EventType.AFTER_REGISTRY); // Keep this message for tests cases work. LOGGER.warn("ServiceComb is ready."); } private void createProducerMicroserviceMeta() { String microserviceName = BootStrapProperties.readServiceName(environment); producerMicroserviceMeta = new MicroserviceMeta(this, BootStrapProperties.readApplication(environment), microserviceName, false); producerMicroserviceMeta.setProviderFilterChain(filterChainsManager.findProducerChain( BootStrapProperties.readApplication(environment), microserviceName)); producerMicroserviceMeta.setMicroserviceVersionsMeta(new MicroserviceVersionsMeta(this)); } /** * not allow throw any exception * even some step throw exception, must catch it and go on, otherwise shutdown process will be broken. */ public synchronized void destroy() { if (SCBStatus.UP.equals(status) || SCBStatus.STARTING.equals(status)) { LOGGER.info("ServiceComb is closing now..."); doDestroy(); status = SCBStatus.DOWN; LOGGER.info("ServiceComb had closed"); } } private void doDestroy() { //Step 0: turn down the status of this instance in service center, // so that the consumers can remove this instance record in advance turnDownInstanceStatus(); blockShutDownOperationForConsumerRefresh(); //Step 1: notify all component stop invoke via BEFORE_CLOSE Event safeTriggerEvent(EventType.BEFORE_CLOSE); //Step 2: forbid create new consumer invocation status = SCBStatus.STOPPING; //Step 3: Unregister microservice instance from Service Center and close vertx // Forbidden other consumers find me registrationManager.destroy(); discoveryManager.destroy(); //Step 4: wait all invocation finished try { validAllInvocationFinished(); } catch (InterruptedException e) { LOGGER.error("wait all invocation finished interrupted", e); } //Step 5: destroy config source // only be null for some test cases if (priorityPropertyManager != null) { priorityPropertyManager.close(); } //Step 6: Stop vertx to prevent blocking exit // delete the following one line when every refactor is done. VertxUtils.blockCloseVertxByName("transport"); HttpClients.destroy(); //Step 7: notify all component do clean works via AFTER_CLOSE Event safeTriggerEvent(EventType.AFTER_CLOSE); } private void turnDownInstanceStatus() { try { registrationManager.updateMicroserviceInstanceStatus(MicroserviceInstanceStatus.DOWN); } catch (Throwable e) { LOGGER.warn("turn down instance status fail: {}", e.getMessage()); } } private void blockShutDownOperationForConsumerRefresh() { try { long turnDownWaitSeconds = environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); if (turnDownWaitSeconds <= 0) { return; } Thread.sleep(TimeUnit.SECONDS.toMillis(turnDownWaitSeconds)); } catch (InterruptedException e) { LOGGER.warn("failed to block the shutdown procedure", e); } } private void validAllInvocationFinished() throws InterruptedException { long start = System.currentTimeMillis(); while (true) { long remaining = invocationStartedCounter.get() - invocationFinishedCounter.get(); if (remaining <= 0) { return; } if (System.currentTimeMillis() - start > TimeUnit.SECONDS.toMillis(30)) { LOGGER.error("wait for all requests timeout, abandon waiting, remaining requests: {}.", remaining); return; } TimeUnit.SECONDS.sleep(1); } } public void ensureStatusUp() { SCBStatus currentStatus = getStatus(); if (!SCBStatus.UP.equals(currentStatus)) { String message = "The request is rejected. Cannot process the request due to STATUS = " + currentStatus; throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData(message)); } } public CompletableFuture getOrCreateReferenceConfigAsync( String microserviceName) { return referenceConfigManager.getOrCreateReferenceConfigAsync(this, microserviceName); } public MicroserviceReferenceConfig getOrCreateReferenceConfig( String microserviceName) { ensureStatusUp(); return referenceConfigManager.getOrCreateReferenceConfig(this, microserviceName); } public MicroserviceMeta getProducerMicroserviceMeta() { return producerMicroserviceMeta; } public void setProducerMicroserviceMeta(MicroserviceMeta producerMicroserviceMeta) { this.producerMicroserviceMeta = producerMicroserviceMeta; } public static class CreateMicroserviceMetaEvent { private final MicroserviceMeta microserviceMeta; public CreateMicroserviceMetaEvent(MicroserviceMeta microserviceMeta) { this.microserviceMeta = microserviceMeta; } public MicroserviceMeta getMicroserviceMeta() { return this.microserviceMeta; } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/SCBStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; //DOWN -> STARTING -> UP -> STOPPING -> DOWN public enum SCBStatus { //Chassis is Down DOWN, //Chassis is Starting (progressing) STARTING, //Chassis is Running UP, //Chassis is Stopping (progressing) STOPPING, //Chassis Init Failed FAILED } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.apache.servicecomb.core.bootup.ConfigurationProblemsCollector; import org.apache.servicecomb.core.bootup.FilterChainCollector; import org.apache.servicecomb.core.bootup.ServiceInformationCollector; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.executor.GroupExecutor; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.core.provider.RegistryOpenAPIRegistry; import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; import org.apache.servicecomb.core.provider.consumer.ReferenceConfigManager; import org.apache.servicecomb.core.provider.producer.ProducerBootListener; import org.apache.servicecomb.core.transport.TransportManager; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration @SuppressWarnings("unused") public class ServiceCombCoreConfiguration { @Bean public SCBApplicationListener scbApplicationListener(SCBEngine scbEngine) { SCBApplicationListener scbApplicationListener = new SCBApplicationListener(scbEngine); scbApplicationListener.setInitEventClass(ApplicationReadyEvent.class); return scbApplicationListener; } @Bean public SCBEngine scbEngine() { return new SCBEngine(); } @Bean public ConsumerProviderManager scbConsumerProviderManager(Environment environment, OpenAPIRegistryManager openAPIRegistryManager) { return new ConsumerProviderManager(environment, openAPIRegistryManager); } @Bean public ReferenceConfigManager scbReferenceConfigManager() { return new ReferenceConfigManager(); } @Bean public OpenAPIRegistryManager scbOpenAPIRegistryManager() { return new OpenAPIRegistryManager(); } @Bean public LocalOpenAPIRegistry scbLocalOpenAPIRegistry(Environment environment) { return new LocalOpenAPIRegistry(environment); } @Bean public RegistryOpenAPIRegistry scbRegistryOpenAPIRegistry() { return new RegistryOpenAPIRegistry(); } @Bean public ProducerBootListener scbProducerBootListener() { return new ProducerBootListener(); } @Bean public FilterChainCollector scbFilterChainCollector() { return new FilterChainCollector(); } @Bean public ConfigurationProblemsCollector scbConfigurationProblemsCollector() { return new ConfigurationProblemsCollector(); } @Bean public ServiceInformationCollector scbServiceInformationCollector() { return new ServiceInformationCollector(); } @Bean public ExecutorManager scbExecutorManager() { return new ExecutorManager(); } @Bean(value = {"cse.executor.groupThreadPool", "cse.executor.default", "servicecomb.executor.groupThreadPool"}) public GroupExecutor scbGroupExecutor(Environment environment) { GroupExecutor groupExecutor = new GroupExecutor(environment); groupExecutor.init(); return groupExecutor; } @Bean public TransportManager scbTransportManager() { return new TransportManager(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/Transport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.springframework.core.env.Environment; public interface Transport { String getName(); default int getOrder() { return 0; } default boolean canInit() { return true; } boolean init() throws Exception; void setEnvironment(Environment environment); /* * endpoint的格式为 URI,比如rest://192.168.1.1:8080 */ Object parseAddress(String endpoint); /* * 本transport的监听地址 */ Endpoint getEndpoint(); /* * 用于上报到服务中心,要求是其他节点可访问的地址 */ Endpoint getPublishEndpoint() throws Exception; } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/annotation/Transport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.annotation; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import org.springframework.stereotype.Component; @Inherited @Documented @Retention(RUNTIME) @Target({TYPE, METHOD, ANNOTATION_TYPE}) @Component public @interface Transport { /** * Transport name. e.g. rest, highway. */ String name(); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootstrap; import java.util.Collections; import java.util.List; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.springframework.core.env.Environment; public class SCBBootstrap { public static SCBEngine createSCBEngineForTest(Environment environment) { LegacyPropertyFactory.setEnvironment(environment); RegistrationManager registrationManager = new RegistrationManager(Collections.emptyList()); DiscoveryManager discoveryManager = new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing())); registrationManager.init(); discoveryManager.init(); SCBEngine result = new SCBEngineForTest(environment); result.setDiscoveryManager(discoveryManager); result.setRegistrationManager(registrationManager); result.setBootListeners(Collections.emptyList()); result.setBootUpInformationCollectors(Collections.emptyList()); result.setExecutorManager(new ExecutorManager()); result.setTransportManager(new TransportManager()); result.setEnvironment(environment); OpenAPIRegistryManager openAPIRegistryManager = new OpenAPIRegistryManager(); openAPIRegistryManager.setOpenAPIRegistries(List.of(new LocalOpenAPIRegistry(environment))); result.setConsumerProviderManager(new ConsumerProviderManager(environment, openAPIRegistryManager)); result.setOpenAPIRegistryManager(openAPIRegistryManager); return result; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootstrap; import java.util.List; import org.apache.servicecomb.config.priority.ConfigObjectFactory; import org.apache.servicecomb.config.priority.PriorityPropertyFactory; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.filter.FilterChainsManager; import org.apache.servicecomb.core.filter.impl.EmptyFilter; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.springframework.core.env.Environment; /** * not depend on remote service registry and spring context */ public class SCBEngineForTest extends SCBEngine { public SCBEngineForTest(Environment environment) { EmptyFilter emptyFilter = new EmptyFilter(); emptyFilter.setEnvironment(environment); setFilterChainsManager(new FilterChainsManager() .setProviderFilters(List.of(emptyFilter)) .setConsumerFilters(List.of(emptyFilter)) .setEdgeFilters(List.of(emptyFilter))); PriorityPropertyFactory propertyFactory = new PriorityPropertyFactory(environment); ConfigObjectFactory configObjectFactory = new ConfigObjectFactory(propertyFactory); setPriorityPropertyManager(new PriorityPropertyManager(configObjectFactory)); setEnvironment(environment); } @Override public synchronized void destroy() { super.destroy(); ReflectUtils.setField(SCBEngine.class, null, "INSTANCE", null); EventManager.eventBus = new SimpleEventBus(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootup; import org.apache.servicecomb.core.SCBEngine; import org.springframework.core.Ordered; public interface BootUpInformationCollector extends Ordered { default String collect(SCBEngine engine) { return collect(); } String collect(); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootup; import org.apache.servicecomb.foundation.common.event.AlarmEvent; public class ConfigurationProblemsAlarmEvent extends AlarmEvent { private final String problems; public ConfigurationProblemsAlarmEvent(Type type, String problems) { super(type); this.problems = problems; } public String getProblems() { return problems; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootup; import java.util.Arrays; import java.util.Set; import java.util.regex.Pattern; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.foundation.common.event.AlarmEvent.Type; import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.core.env.Environment; /** * Detect deprecated and wrong usages of configurations * and print warning messages * and sending ConfigurationProblemsAlarmEvent. */ public class ConfigurationProblemsCollector implements BootUpInformationCollector { private static final String SERVICE_NAME_PATTERN_STRING = "([A-Za-z])|([A-Za-z][A-Za-z0-9_\\-.]*[A-Za-z0-9])"; private static final Pattern SERVICE_NAME_PATTERN = Pattern.compile(SERVICE_NAME_PATTERN_STRING); @Override public String collect(SCBEngine engine) { StringBuilder result = new StringBuilder(); collectCsePrefix(engine.getEnvironment(), result); collectServiceDefinition(engine.getEnvironment(), result); collectServiceDefinitionValidation(engine.getEnvironment(), result); collectTimeoutConfiguration(engine.getEnvironment(), result); collectIsolationConfiguration(engine.getEnvironment(), result); if (result.isEmpty()) { return null; } String warnings = "[WARN]Configurations warnings:\n" + result; EventManager.post(new ConfigurationProblemsAlarmEvent(Type.OPEN, warnings)); return warnings; } private void collectServiceDefinitionValidation(Environment environment, StringBuilder result) { String application = BootStrapProperties.readApplication(environment); if (!SERVICE_NAME_PATTERN.matcher(application).matches()) { result.append("application does not match pattern ").append(SERVICE_NAME_PATTERN_STRING).append("."); } String serviceName = BootStrapProperties.readServiceName(environment); if (!SERVICE_NAME_PATTERN.matcher(serviceName).matches()) { result.append("service name does not match pattern ").append(SERVICE_NAME_PATTERN_STRING).append("."); } } private void collectIsolationConfiguration(Environment environment, StringBuilder result) { int percentage = environment.getProperty( "servicecomb.loadbalance.isolation.errorThresholdPercentage", int.class, -1); int continuous = environment.getProperty( "servicecomb.loadbalance.isolation.continuousFailureThreshold", int.class, -1); if (percentage == -1 && continuous == -1) { return; } result.append("Configuration `servicecomb.loadbalance.isolation.*` is removed, use governance instead. " + "See https://servicecomb.apache.org/references/java-chassis/" + "zh_CN/references-handlers/governance-best-practise.html"); } private void collectTimeoutConfiguration(Environment environment, StringBuilder result) { int keepAliveTimeoutInSeconds = environment.getProperty( "servicecomb.rest.client.connection.keepAliveTimeoutInSeconds", int.class, 60); int idleTimeoutInSeconds = environment.getProperty( "servicecomb.rest.client.connection.idleTimeoutInSeconds", int.class, 150); if (keepAliveTimeoutInSeconds >= idleTimeoutInSeconds) { result.append("Configuration `servicecomb.rest.client.connection.keepAliveTimeoutInSeconds` is longer than " + "servicecomb.rest.client.connection.idleTimeoutInSeconds."); result.append("[").append(keepAliveTimeoutInSeconds).append(",").append(idleTimeoutInSeconds).append("]\n"); } keepAliveTimeoutInSeconds = environment.getProperty( "servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds", int.class, 60); idleTimeoutInSeconds = environment.getProperty( "servicecomb.rest.client.http2.connection.idleTimeoutInSeconds", int.class, 150); if (keepAliveTimeoutInSeconds >= idleTimeoutInSeconds) { result.append("Configuration `servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds` is longer than " + "servicecomb.rest.client.http2.connection.idleTimeoutInSeconds."); result.append("[").append(keepAliveTimeoutInSeconds).append(",").append(idleTimeoutInSeconds).append("]\n"); } } private void collectServiceDefinition(Environment environment, StringBuilder result) { if (environment.getProperty("APPLICATION_ID") != null) { result.append("Configurations `APPLICATION_ID` is deprecated, " + "use `servicecomb.service.application` instead.\n"); } Set names = ConfigUtil.propertiesWithPrefix(environment, "service_description."); if (!names.isEmpty()) { result.append("Configurations with prefix `service_description` is deprecated, " + "use `servicecomb.service` instead. Find keys "); result.append(Arrays.toString(names.toArray())); result.append("\n"); } } private void collectCsePrefix(Environment environment, StringBuilder result) { Set names = ConfigUtil.propertiesWithPrefix(environment, "cse."); if (!names.isEmpty()) { result.append("Configurations with prefix `cse` is deprecated, use `servicecomb` instead. Find keys "); result.append(Arrays.toString(names.toArray())); result.append("\n"); } } @Override public String collect() { return null; } @Override public int getOrder() { return 1000; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootup/FilterChainCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootup; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.filter.FilterChainsManager; public class FilterChainCollector implements BootUpInformationCollector { @Override public String collect(SCBEngine engine) { FilterChainsManager mgr = engine.getFilterChainsManager(); return mgr.collectResolvedChains(); } @Override public String collect() { return null; } @Override public int getOrder() { return 300; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/bootup/ServiceInformationCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.bootup; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.RegistrationManager; import org.springframework.beans.factory.annotation.Autowired; public class ServiceInformationCollector implements BootUpInformationCollector { private RegistrationManager registrationManager; private DiscoveryManager discoveryManager; public ServiceInformationCollector() { } @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; } @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Override public String collect() { return registrationManager.info() + "\n" + discoveryManager.info(); } @Override public int getOrder() { return 200; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/ConsumerMicroserviceVersionsMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import org.apache.servicecomb.core.SCBEngine; public class ConsumerMicroserviceVersionsMeta extends MicroserviceVersionsMeta { public ConsumerMicroserviceVersionsMeta(SCBEngine scbEngine) { super(scbEngine); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/CoreMetaUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; public final class CoreMetaUtils { public static final String SWAGGER_PRODUCER = "scb_swagger-producer"; private CoreMetaUtils() { } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/InvocationRuntimeType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.lang.reflect.Method; import java.lang.reflect.Type; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.response.ResponsesMeta; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; /** * An InvocationRuntimeType indicates the associated java type information of this invocation. * * For producer, java type information NOT be changed for each invocation. * * For Consumer, java type information depend on method signature, or not available when in edge or * invoked by raw type way like RestTemplate or InvokerUtils. */ public class InvocationRuntimeType { private Class associatedClass; private Method associatedMethod; private final ResponsesMeta responsesMeta; private ArgumentsMapper argumentsMapper; public InvocationRuntimeType(Class associatedClass, Method associatedMethod, ResponsesMeta responsesMeta, ArgumentsMapper argumentsMapper) { this.associatedClass = associatedClass; this.associatedMethod = associatedMethod; this.argumentsMapper = argumentsMapper; this.responsesMeta = responsesMeta; } public InvocationRuntimeType(ResponsesMeta responsesMeta) { this.responsesMeta = responsesMeta; } public Class getAssociatedClass() { return this.associatedClass; } public Method getAssociatedMethod() { return this.associatedMethod; } public ArgumentsMapper getArgumentsMapper() { return this.argumentsMapper; } public JavaType findResponseType(int statusCode) { return responsesMeta.findResponseType(statusCode); } public void setSuccessResponseType(JavaType javaType) { if (javaType != null) { // when javaType is null , using swagger type, do not override responsesMeta.setResponseType(Status.OK.getStatusCode(), javaType); } } public void setSuccessResponseType(Type type) { if (type != null) { // when javaType is null , using swagger type, do not override responsesMeta.setResponseType(Status.OK.getStatusCode(), TypeFactory.defaultInstance().constructType(type)); } } public void setAssociatedClass(Class associatedClass) { this.associatedClass = associatedClass; } public void setAssociatedMethod(Method associatedMethod) { this.associatedMethod = associatedMethod; } public void setArgumentsMapper(ArgumentsMapper argumentsMapper) { this.argumentsMapper = argumentsMapper; } public boolean isRawConsumer() { return this.associatedClass == null; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.VendorExtensions; import io.swagger.v3.oas.models.OpenAPI; public class MicroserviceMeta { private final SCBEngine scbEngine; private MicroserviceVersionsMeta microserviceVersionsMeta; private final String appId; private final String microserviceName; // key is schemaId, this is all schemas private final Map schemaMetas = new HashMap<>(); // key is OperationMeta.getMicroserviceQualifiedName() private final Map operationMetas = new HashMap<>(); // Used to indicate configuration items type. EDGE & CONSUMER chain are all consumer. private final boolean consumer; private FilterNode consumerFilterChain = FilterNode.EMPTY; private FilterNode providerFilterChain = FilterNode.EMPTY; private FilterNode edgeFilterChain = FilterNode.EMPTY; private final VendorExtensions vendorExtensions = new VendorExtensions(); public MicroserviceMeta(SCBEngine scbEngine, String application, String serviceName, boolean consumer) { this.scbEngine = scbEngine; this.appId = application; this.microserviceName = serviceName; this.consumer = consumer; } public MicroserviceVersionsMeta getMicroserviceVersionsMeta() { return microserviceVersionsMeta; } public void setMicroserviceVersionsMeta(MicroserviceVersionsMeta microserviceVersionsMeta) { this.microserviceVersionsMeta = microserviceVersionsMeta; } public SCBEngine getScbEngine() { return scbEngine; } public boolean isConsumer() { return consumer; } public String getMicroserviceName() { return microserviceName; } public String getAppId() { return appId; } public SchemaMeta registerSchemaMeta(String schemaId, OpenAPI swagger) { SchemaMeta schemaMeta = new SchemaMeta(this, schemaId, swagger); if (schemaMetas.putIfAbsent(schemaMeta.getSchemaId(), schemaMeta) != null) { throw new IllegalStateException(String.format( "failed to register SchemaMeta caused by duplicated schemaId, appId=%s, microserviceName=%s, schemaId=%s.", appId, microserviceName, schemaMeta.getSchemaId())); } schemaMeta.getOperations().values() .forEach(operationMeta -> operationMetas.put(operationMeta.getMicroserviceQualifiedName(), operationMeta)); return schemaMeta; } public Map operationMetas() { return operationMetas; } public Collection getOperations() { return operationMetas.values(); } public SchemaMeta ensureFindSchemaMeta(String schemaId) { SchemaMeta schemaMeta = schemaMetas.get(schemaId); if (schemaMeta == null) { throw new IllegalStateException(String.format( "failed to find SchemaMeta by schemaId, appId=%s, microserviceName=%s, schemaId=%s.", appId, microserviceName, schemaId)); } return schemaMeta; } public SchemaMeta findSchemaMeta(String schemaId) { return schemaMetas.get(schemaId); } public Map getSchemaMetas() { return schemaMetas; } public void putExtData(String key, Object data) { vendorExtensions.put(key, data); } public T getExtData(String key) { return vendorExtensions.get(key); } public FilterNode getConsumerFilterChain() { return consumerFilterChain; } public void setConsumerFilterChain(FilterNode consumerFilterChain) { this.consumerFilterChain = consumerFilterChain; } public FilterNode getProviderFilterChain() { return providerFilterChain; } public void setProviderFilterChain(FilterNode providerFilterChain) { this.providerFilterChain = providerFilterChain; } public FilterNode getEdgeFilterChain() { return edgeFilterChain; } public void setEdgeFilterChain(FilterNode edgeFilterChain) { this.edgeFilterChain = edgeFilterChain; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceVersionsMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.util.Map; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class MicroserviceVersionsMeta { protected final SCBEngine scbEngine; // key is operationMeta.getMicroserviceQualifiedName() private final Map configs = new ConcurrentHashMapEx<>(); public MicroserviceVersionsMeta(SCBEngine scbEngine) { this.scbEngine = scbEngine; } public OperationConfig getOrCreateOperationConfig(OperationMeta operationMeta) { return configs.computeIfAbsent(operationMeta.getMicroserviceQualifiedName(), name -> createOperationConfig(operationMeta)); } private OperationConfig createOperationConfig(OperationMeta operationMeta) { boolean consumer = operationMeta.getMicroserviceMeta().isConsumer(); return scbEngine.getPriorityPropertyManager().createConfigObject( OperationConfig.class, "op-any-priority", consumer ? OperationConfig.CONSUMER_OP_ANY_PRIORITY : OperationConfig.PRODUCER_OP_ANY_PRIORITY, "consumer-op-any_priority", OperationConfig.CONSUMER_OP_ANY_PRIORITY, "producer-op-any_priority", OperationConfig.PRODUCER_OP_ANY_PRIORITY, "op-priority", consumer ? OperationConfig.CONSUMER_OP_PRIORITY : OperationConfig.PRODUCER_OP_PRIORITY, "consumer-op-priority", OperationConfig.CONSUMER_OP_PRIORITY, "producer-op-priority", OperationConfig.PRODUCER_OP_PRIORITY, "consumer-producer", consumer ? "Consumer" : "Provider", "consumer-provider", consumer ? "Consumer" : "Provider", "service", operationMeta.getMicroserviceName(), "schema", operationMeta.getSchemaId(), "operation", operationMeta.getOperationId()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/OperationConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.apache.servicecomb.core.CoreConst; @InjectProperties(prefix = "servicecomb") public class OperationConfig { public static final List CONSUMER_OP_ANY_PRIORITY = Arrays.asList( "${service}.${schema}.${operation}", "${service}.${schema}", "${service}"); public static final List PRODUCER_OP_ANY_PRIORITY = Arrays.asList( "${schema}.${operation}", "${schema}"); public static final List CONSUMER_OP_PRIORITY = Arrays.asList( ".${service}.${schema}.${operation}", ".${service}.${schema}", ".${service}", ""); public static final List PRODUCER_OP_PRIORITY = Arrays.asList( ".${schema}.${operation}", ".${schema}", ""); @InjectProperty(keys = {"metrics.${consumer-producer}.invocation.slow.enabled${op-priority}", "${consumer-producer}.invocation.slow.enabled${op-priority}"}, defaultValue = "false") private boolean slowInvocationEnabled; @InjectProperty(keys = {"metrics.${consumer-producer}.invocation.slow.msTime${op-priority}", "${consumer-producer}.invocation.slow.msTime${op-priority}"}, defaultValue = "1000") private long msSlowInvocation; private long nanoSlowInvocation; /** * consumer request timeout */ @InjectProperty(keys = {"request.${op-any-priority}.timeout", "request.timeout"}, defaultValue = "30000") private long msRequestTimeout; /** * Invocation timeout. */ @InjectProperty(keys = {"invocation.${op-any-priority}.timeout", "invocation.timeout"}, defaultValue = "-1") private long msInvocationTimeout; private long nanoInvocationTimeout; /** * whether to remove certain headers from the 3rd party invocations */ @InjectProperty(keys = {"request.clientRequestHeaderFilterEnabled${consumer-op-priority}"}, defaultValue = "false") private boolean clientRequestHeaderFilterEnabled = false; /** * producer wait in thread pool timeout */ private final Map nanoRequestWaitInPoolTimeoutByTransport = new HashMap<>(); @InjectProperty(keys = "Provider.requestWaitInPoolTimeout${op-priority}", defaultValue = "30000") private long msDefaultRequestWaitInPoolTimeout; private long nanoDefaultRequestWaitInPoolTimeout; @InjectProperty(keys = { "Provider.requestWaitInPoolTimeout${op-priority}", "highway.server.requestWaitInPoolTimeout"}, defaultValue = "30000") private long msHighwayRequestWaitInPoolTimeout; private long nanoHighwayRequestWaitInPoolTimeout; @InjectProperty(keys = { "Provider.requestWaitInPoolTimeout${op-priority}", "rest.server.requestWaitInPoolTimeout"}, defaultValue = "30000") private long msRestRequestWaitInPoolTimeout; private long nanoRestRequestWaitInPoolTimeout; @InjectProperty(keys = { "operation${op-priority}.transport", // Deprecated "references.transport${op-priority}" }) private String transport; @InjectProperty(keys = {"governance.${op-any-priority}.matchType", "governance.matchType"}, defaultValue = "rest") private String governanceMatchType; public boolean isSlowInvocationEnabled() { return slowInvocationEnabled; } public void setSlowInvocationEnabled(boolean slowInvocationEnabled) { this.slowInvocationEnabled = slowInvocationEnabled; } public long getMsSlowInvocation() { return msSlowInvocation; } public void setMsSlowInvocation(long msSlowInvocation) { this.msSlowInvocation = msSlowInvocation; this.nanoSlowInvocation = TimeUnit.MILLISECONDS.toNanos(msSlowInvocation); } public long getNanoSlowInvocation() { return nanoSlowInvocation; } public long getMsRequestTimeout() { return msRequestTimeout; } public void setMsRequestTimeout(long msRequestTimeout) { this.msRequestTimeout = msRequestTimeout; } public long getNanoRequestWaitInPoolTimeout(String transport) { return nanoRequestWaitInPoolTimeoutByTransport.getOrDefault(transport, nanoDefaultRequestWaitInPoolTimeout); } public void registerRequestWaitInPoolTimeout(String transport, long msTimeout) { nanoRequestWaitInPoolTimeoutByTransport.put(transport, TimeUnit.MILLISECONDS.toNanos(msTimeout)); } public long getMsDefaultRequestWaitInPoolTimeout() { return msDefaultRequestWaitInPoolTimeout; } public void setMsDefaultRequestWaitInPoolTimeout(long msDefaultRequestWaitInPoolTimeout) { this.msDefaultRequestWaitInPoolTimeout = msDefaultRequestWaitInPoolTimeout; this.nanoDefaultRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msDefaultRequestWaitInPoolTimeout); } public long getNanoDefaultRequestWaitInPoolTimeout() { return nanoDefaultRequestWaitInPoolTimeout; } public long getMsHighwayRequestWaitInPoolTimeout() { return msHighwayRequestWaitInPoolTimeout; } public void setMsHighwayRequestWaitInPoolTimeout(long msHighwayRequestWaitInPoolTimeout) { this.msHighwayRequestWaitInPoolTimeout = msHighwayRequestWaitInPoolTimeout; this.nanoHighwayRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msHighwayRequestWaitInPoolTimeout); registerRequestWaitInPoolTimeout(CoreConst.HIGHWAY, msHighwayRequestWaitInPoolTimeout); } public long getNanoHighwayRequestWaitInPoolTimeout() { return nanoHighwayRequestWaitInPoolTimeout; } public long getMsRestRequestWaitInPoolTimeout() { return msRestRequestWaitInPoolTimeout; } public void setMsRestRequestWaitInPoolTimeout(long msRestRequestWaitInPoolTimeout) { this.msRestRequestWaitInPoolTimeout = msRestRequestWaitInPoolTimeout; this.nanoRestRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msRestRequestWaitInPoolTimeout); registerRequestWaitInPoolTimeout(CoreConst.RESTFUL, msRestRequestWaitInPoolTimeout); } public long getNanoRestRequestWaitInPoolTimeout() { return nanoRestRequestWaitInPoolTimeout; } public long getMsInvocationTimeout() { return msInvocationTimeout; } public void setMsInvocationTimeout(long msInvocationTimeout) { this.msInvocationTimeout = msInvocationTimeout; this.nanoInvocationTimeout = TimeUnit.MILLISECONDS.toNanos(msInvocationTimeout); } public String getGovernanceMatchType() { return governanceMatchType; } public void setGovernanceMatchType(String governanceMatchType) { this.governanceMatchType = governanceMatchType; } public long getNanoInvocationTimeout() { return this.nanoInvocationTimeout; } public boolean isClientRequestHeaderFilterEnabled() { return clientRequestHeaderFilterEnabled; } public void setClientRequestHeaderFilterEnabled(boolean clientRequestHeaderFilterEnabled) { this.clientRequestHeaderFilterEnabled = clientRequestHeaderFilterEnabled; } public String getTransport() { return transport; } public void setTransport(String transport) { if (transport == null) { transport = CoreConst.ANY_TRANSPORT; } this.transport = transport; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.util.concurrent.Executor; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.foundation.common.VendorExtensions; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.response.ResponsesMeta; import io.swagger.v3.oas.models.Operation; public class OperationMeta { private SchemaMeta schemaMeta; // schemaId.operation private String schemaQualifiedName; // microserviceName.schemaId.operation private String microserviceQualifiedName; private String httpMethod; private String operationPath; private Operation swaggerOperation; // run in this executor private Executor executor; private final ResponsesMeta responsesMeta = new ResponsesMeta(); private final VendorExtensions vendorExtensions = new VendorExtensions(); public OperationMeta init(SchemaMeta schemaMeta, SwaggerOperation swaggerOperation) { this.schemaMeta = schemaMeta; this.schemaQualifiedName = schemaMeta.getSchemaId() + "." + swaggerOperation.getOperationId(); this.microserviceQualifiedName = schemaMeta.getMicroserviceQualifiedName() + "." + swaggerOperation.getOperationId(); this.httpMethod = swaggerOperation.getHttpMethod().name(); this.operationPath = swaggerOperation.getPath(); this.swaggerOperation = swaggerOperation.getOperation(); this.executor = schemaMeta.getMicroserviceMeta().getScbEngine().getExecutorManager().findExecutor(this); this.responsesMeta.init(schemaMeta.getSwagger(), swaggerOperation.getOperation()); return this; } public void setSwaggerProducerOperation(SwaggerProducerOperation swaggerProducerOperation) { this.putExtData(CoreConst.PRODUCER_OPERATION, swaggerProducerOperation); } public SwaggerProducerOperation getSwaggerProducerOperation() { return (SwaggerProducerOperation) this.getExtData(CoreConst.PRODUCER_OPERATION); } public OperationConfig getConfig() { return schemaMeta.getMicroserviceMeta().getMicroserviceVersionsMeta().getOrCreateOperationConfig(this); } public String getHttpMethod() { return httpMethod; } public String getOperationPath() { return operationPath; } public Operation getSwaggerOperation() { return swaggerOperation; } public ResponsesMeta getResponsesMeta() { // TODO : this method now called by highway, and highway always use swagger type // in the future improvement , highway can use runtime type and this method can be removed return responsesMeta; } public int parameterCount() { int result = 0; if (swaggerOperation.getRequestBody() != null) { result++; } if (swaggerOperation.getParameters() != null) { result += swaggerOperation.getParameters().size(); } return result; } private ResponsesMeta cloneResponseMeta() { ResponsesMeta result = new ResponsesMeta(); this.responsesMeta.cloneTo(result); return result; } public MicroserviceMeta getMicroserviceMeta() { return schemaMeta.getMicroserviceMeta(); } public SchemaMeta getSchemaMeta() { return schemaMeta; } public String getSchemaQualifiedName() { return schemaQualifiedName; } public String getMicroserviceQualifiedName() { return microserviceQualifiedName; } public String getMicroserviceName() { return schemaMeta.getMicroserviceName(); } public String getSchemaId() { return schemaMeta.getSchemaId(); } public String getOperationId() { return swaggerOperation.getOperationId(); } // invoker make sure idx is valid public String getParamName(int idx) { return swaggerOperation.getParameters().get(idx).getName(); } public void putExtData(String key, Object data) { vendorExtensions.put(key, data); } public T getExtData(String key) { return vendorExtensions.get(key); } public VendorExtensions getVendorExtensions() { return vendorExtensions; } public Executor getExecutor() { return executor; } public void setExecutor(Executor executor) { this.executor = executor; } public InvocationRuntimeType buildBaseProviderRuntimeType() { SwaggerProducerOperation swaggerProducerOperation = this.getSwaggerProducerOperation(); return new InvocationRuntimeType(swaggerProducerOperation.getProducerClass(), swaggerProducerOperation.getProducerMethod(), this.cloneResponseMeta(), swaggerProducerOperation.getArgumentsMapper()); } public InvocationRuntimeType buildBaseConsumerRuntimeType() { return new InvocationRuntimeType(this.cloneResponseMeta()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/definition/SchemaMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.VendorExtensions; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.oas.models.OpenAPI; public class SchemaMeta { private static final Logger LOGGER = LoggerFactory.getLogger(SchemaMeta.class); private final MicroserviceMeta microserviceMeta; private final OpenAPI swagger; private final String schemaId; // microserviceName.schemaId private final String microserviceQualifiedName; private final Map operations = new HashMap<>(); private final VendorExtensions vendorExtensions = new VendorExtensions(); public SchemaMeta(MicroserviceMeta microserviceMeta, String schemaId, OpenAPI swagger) { this.microserviceMeta = microserviceMeta; this.schemaId = schemaId; this.swagger = swagger; this.microserviceQualifiedName = microserviceMeta.getMicroserviceName() + "." + schemaId; try { initOperationMetas(); } catch (Throwable e) { LOGGER.error("Unhandled exception to {}.", microserviceQualifiedName, e); throw e; } } private SchemaMeta initOperationMetas() { SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); for (SwaggerOperation swaggerOperation : swaggerOperations.getOperations().values()) { operations.put(swaggerOperation.getOperationId(), new OperationMeta().init(this, swaggerOperation)); } return this; } public MicroserviceMeta getMicroserviceMeta() { return microserviceMeta; } public OpenAPI getSwagger() { return swagger; } public String getAppId() { return microserviceMeta.getAppId(); } public String getMicroserviceName() { return microserviceMeta.getMicroserviceName(); } public String getSchemaId() { return schemaId; } public String getMicroserviceQualifiedName() { return microserviceQualifiedName; } public Map getOperations() { return operations; } public void putExtData(String key, Object data) { vendorExtensions.put(key, data); } public T getExtData(String key) { return vendorExtensions.get(key); } public OperationMeta findOperation(String operationId) { return operations.get(operationId); } public OperationMeta ensureFindOperation(String operationId) { OperationMeta value = operations.get(operationId); if (value == null) { throw new IllegalStateException(String .format("Can not find OperationMeta, microserviceName=%s, schemaId=%s, operationId=%s.", getMicroserviceName(), getSchemaId(), operationId)); } return value; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationBaseEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationBaseEvent { private final Invocation invocation; public InvocationBaseEvent(Invocation invocation) { this.invocation = invocation; } public Invocation getInvocation() { return invocation; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessFinishEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationBusinessFinishEvent extends InvocationBaseEvent { public InvocationBusinessFinishEvent(Invocation invocation) { super(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodFinishEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationBusinessMethodFinishEvent extends InvocationBaseEvent { public InvocationBusinessMethodFinishEvent(Invocation invocation) { super(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodStartEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationBusinessMethodStartEvent extends InvocationBaseEvent { public InvocationBusinessMethodStartEvent(Invocation invocation) { super(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationEncodeResponseStartEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; public class InvocationEncodeResponseStartEvent extends InvocationWithResponseEvent { public InvocationEncodeResponseStartEvent(Invocation invocation, Response response) { super(invocation, response); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; public class InvocationFinishEvent extends InvocationWithResponseEvent { public InvocationFinishEvent(Invocation invocation, Response response) { super(invocation, response); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationStartEvent extends InvocationBaseEvent { public InvocationStartEvent(Invocation invocation) { super(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationStartSendRequestEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationStartSendRequestEvent extends InvocationBaseEvent { public InvocationStartSendRequestEvent(Invocation invocation) { super(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationTimeoutCheckEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; public class InvocationTimeoutCheckEvent extends InvocationBaseEvent { public InvocationTimeoutCheckEvent(Invocation invocation) { super(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/InvocationWithResponseEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; public class InvocationWithResponseEvent extends InvocationBaseEvent { protected Response response; public InvocationWithResponseEvent(Invocation invocation, Response response) { super(invocation); this.response = response; } public Response getResponse() { return response; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/event/ServerAccessLogEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import io.vertx.ext.web.RoutingContext; public class ServerAccessLogEvent { private long milliStartTime; private long milliEndTime; private RoutingContext routingContext; /** * If client send request via a short-lived connection, the connection may be closed before the corresponding * access log is generated, and then we can not get valid host ip * may get "0.0.0.0" as result. So we need to get local address before the connection is closed. */ private String localAddress; public ServerAccessLogEvent() { } public long getMilliStartTime() { return milliStartTime; } public ServerAccessLogEvent setMilliStartTime(long milliStartTime) { this.milliStartTime = milliStartTime; return this; } public long getMilliEndTime() { return milliEndTime; } public ServerAccessLogEvent setMilliEndTime(long milliEndTime) { this.milliEndTime = milliEndTime; return this; } public RoutingContext getRoutingContext() { return routingContext; } public ServerAccessLogEvent setRoutingContext(RoutingContext routingContext) { this.routingContext = routingContext; return this; } public String getLocalAddress() { return localAddress; } public ServerAccessLogEvent setLocalAddress(String localAddress) { this.localAddress = localAddress; return this; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CoreExceptionConfiguration { @Bean public Exceptions scbExceptions() { return new Exceptions(); } @Bean public DefaultExceptionProcessor scbDefaultExceptionProcessor() { return new DefaultExceptionProcessor(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/CseException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; public class CseException extends RuntimeException { private static final long serialVersionUID = 8027482777502649656L; private final String code; public CseException(String code, String message) { super(message); this.code = code; } public CseException(String code, String message, Throwable cause) { super(message, cause); this.code = code; } public String getCode() { return code; } @Override public String toString() { return "ServiceDefinitionException Code:" + code + ", Message:" + getMessage(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static jakarta.ws.rs.core.Response.Status.TOO_MANY_REQUESTS; import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_SERVER; import static org.apache.servicecomb.swagger.invocation.InvocationType.CONSUMER; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.ws.rs.core.Response.StatusType; @InjectProperties(prefix = "servicecomb.invocation.exception") public class DefaultExceptionProcessor implements ExceptionProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionProcessor.class); public static final int ORDER = Integer.MAX_VALUE; @JsonIgnore @SuppressWarnings("unchecked") private final List> converters = SPIServiceUtils .getOrLoadSortedService(ExceptionConverter.class).stream() .map(converter -> (ExceptionConverter) converter) .collect(Collectors.toList()); @InjectProperty(keys = "print-stack-trace", defaultValue = "true") protected boolean printStackTrace; @InjectProperty(keys = "print-rate-limit", defaultValue = "false") protected boolean printRateLimit; private final Map, ExceptionConverter> converterCache = new ConcurrentHashMapEx<>(); @Override public int getOrder() { return ORDER; } @Override public boolean isPrintStackTrace() { return printStackTrace; } public DefaultExceptionProcessor setPrintStackTrace(boolean printStackTrace) { this.printStackTrace = printStackTrace; return this; } public boolean isPrintRateLimit() { return printRateLimit; } public DefaultExceptionProcessor setPrintRateLimit(boolean printRateLimit) { this.printRateLimit = printRateLimit; return this; } @SuppressWarnings("unchecked") @Autowired(required = false) public DefaultExceptionProcessor setConverters(List> converters) { converters.forEach(converter -> this.converters.add((ExceptionConverter) converter)); this.converters.sort(Comparator.comparingInt(ExceptionConverter::getOrder)); this.converterCache.clear(); return this; } @Override public InvocationException convert(Invocation invocation, Throwable throwable) { StatusType genericStatus = CONSUMER.equals(invocation.getInvocationType()) ? BAD_REQUEST : INTERNAL_SERVER_ERROR; return convert(invocation, throwable, genericStatus); } @Override public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { Throwable unwrapped = ExceptionFactory.unwrap(throwable); try { ExceptionConverter converter = converterCache.computeIfAbsent(unwrapped.getClass(), clazz -> findConverter(unwrapped)); if (invocation == null) { LOGGER.warn("Convert unknown operation exception {}/{} using {}.", throwable.getClass().getSimpleName(), unwrapped.getClass().getSimpleName(), converter.getClass().getSimpleName()); } else { invocation.getTraceIdLogger().warn("{} Convert operation {} exception {}/{} using {}.", TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), throwable.getClass().getSimpleName(), unwrapped.getClass().getSimpleName(), converter.getClass().getSimpleName()); } return converter.convert(invocation, unwrapped, genericStatus); } catch (Exception e) { LOGGER.error("BUG: ExceptionConverter.convert MUST not throw exception, please fix it.\n" + "original exception :{}" + "converter exception:{}", getStackTrace(throwable), getStackTrace(e)); return new InvocationException(INTERNAL_SERVER_ERROR, new CommonExceptionData(GENERIC_SERVER, INTERNAL_SERVER_ERROR.getReasonPhrase())); } } private ExceptionConverter findConverter(Throwable throwable) { for (ExceptionConverter converter : converters) { if (converter.canConvert(throwable)) { return converter; } } throw new IllegalStateException("never happened: can not find converter for " + throwable.getClass().getName()); } @Override public Response toConsumerResponse(Invocation invocation, Throwable throwable) { InvocationException exception = convert(invocation, throwable, BAD_REQUEST); logConsumerException(invocation, exception); return Response.failResp(exception); } @Override public void logConsumerException(Invocation invocation, InvocationException exception) { if (isIgnoreLog(invocation, exception)) { return; } if (isPrintStackTrace()) { invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}.", TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), invocation.getEndpoint(), exception); return; } invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}, message={}.", TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), invocation.getEndpoint(), ExceptionUtils.getExceptionMessageWithoutTrace(exception)); } @Override public boolean isIgnoreLog(Invocation invocation, InvocationException exception) { if (!isPrintRateLimit() && exception.getStatusCode() == TOO_MANY_REQUESTS.getStatusCode()) { return true; } return false; } @Override public Response toProducerResponse(Invocation invocation, Throwable exception) { InvocationException invocationException = convert(invocation, exception, INTERNAL_SERVER_ERROR); if (invocation != null) { logProducerException(invocation, invocationException); } return Response.createFail(invocationException); } @Override public void logProducerException(Invocation invocation, InvocationException exception) { if (isIgnoreLog(invocation, exception)) { return; } if (isPrintStackTrace()) { invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}.", TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), exception); return; } invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}, message={}.", TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), ExceptionUtils.getExceptionMessageWithoutTrace(exception)); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/ExceptionCodes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; public interface ExceptionCodes { String GENERIC_CLIENT = "SCB.00000000"; String LB_ADDRESS_NOT_FOUND = "SCB.00000001"; String NOT_DEFINED_ANY_SCHEMA = "SCB.00000002"; String DEFAULT_VALIDATE = "SCB.00000003"; String INVOCATION_TIMEOUT = "SCB.00000004"; String GENERIC_SERVER = "SCB.50000000"; } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/ExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import static jakarta.ws.rs.core.Response.Status.Family.CLIENT_ERROR; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_SERVER; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.StatusType; public interface ExceptionConverter extends SPIOrder { static boolean isClient(StatusType status) { return CLIENT_ERROR.equals(status.getFamily()); } static String getGenericCode(StatusType status) { return isClient(status) ? GENERIC_CLIENT : GENERIC_SERVER; } /** * * @param throwable exception will be converted * @return can convert the exception */ boolean canConvert(Throwable throwable); /** * * @param invocation related invocation, will be null only when failed to prepare invocation * @param throwable exception will be converted * @param genericStatus if can not determine the status type, then use this input value * @return converted invocation exception */ InvocationException convert(Invocation invocation, T throwable, StatusType genericStatus); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/ExceptionProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.core.Ordered; import jakarta.ws.rs.core.Response.StatusType; /** * will select the min order instance */ public interface ExceptionProcessor extends Ordered { @Override default int getOrder() { return 0; } boolean isPrintStackTrace(); InvocationException convert(Invocation invocation, Throwable throwable); InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus); boolean isIgnoreLog(Invocation invocation, InvocationException exception); Response toConsumerResponse(Invocation invocation, Throwable throwable); void logConsumerException(Invocation invocation, InvocationException exception); Response toProducerResponse(Invocation invocation, Throwable exception); void logProducerException(Invocation invocation, InvocationException exception); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/ExceptionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import org.apache.servicecomb.foundation.common.RegisterManager; import org.apache.servicecomb.foundation.common.utils.FortifyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExceptionUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionUtils.class); // 异常码 private static final String ERROR_DESC_MGR_MSG = "error desc mgr"; protected static final RegisterManager ERROR_DESC_MGR = new RegisterManager<>(ERROR_DESC_MGR_MSG); private static final String SERVICECOMB_HANDLER_REF_NOT_EXIST = "servicecomb.handler.ref.not.exist"; private static final String SERVICECOMB_PRODUCER_OPERATION_NOT_EXIST = "servicecomb.producer.operation.not.exist"; private static final String SERVICECOMB_LB_NO_AVAILABLE_ADDRESS = "servicecomb.lb.no.available.address"; static { ERROR_DESC_MGR.register(SERVICECOMB_HANDLER_REF_NOT_EXIST, "Handler not exist, id=%s"); ERROR_DESC_MGR.register(SERVICECOMB_PRODUCER_OPERATION_NOT_EXIST, "Producer operation not exist, schemaId=%s, operationName=%s"); ERROR_DESC_MGR.register(SERVICECOMB_LB_NO_AVAILABLE_ADDRESS, "No available address found. microserviceName=%s, version=%s, discoveryGroupName=%s"); } protected ExceptionUtils() { } // TODO:应该改为protected,不允许随便调,所有异常,都必须是强类型的 public static CseException createCseException(String code, Object... args) { String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); CseException exception = new CseException(code, msg); LOGGER.error(FortifyUtils.getErrorInfo(exception)); return exception; } public static CseException createCseException(String code, Throwable cause, Object... args) { String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); CseException exception = new CseException(code, msg, cause); LOGGER.error(FortifyUtils.getErrorInfo(exception)); return exception; } public static CseException producerOperationNotExist(String schemaId, String operationName) { return createCseException(SERVICECOMB_PRODUCER_OPERATION_NOT_EXIST, schemaId, operationName); } public static CseException handlerRefNotExist(String id) { return createCseException(SERVICECOMB_HANDLER_REF_NOT_EXIST, id); } public static CseException lbAddressNotFound(String microserviceName, String microserviceVersionRule, String discoveryGroupName) { return createCseException(SERVICECOMB_LB_NO_AVAILABLE_ADDRESS, microserviceName, microserviceVersionRule, discoveryGroupName); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/Exceptions.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_SERVER; import static org.apache.servicecomb.swagger.invocation.InvocationType.CONSUMER; import java.util.Comparator; import java.util.List; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import jakarta.ws.rs.core.Response.StatusType; public class Exceptions { private static ExceptionProcessor processor = new DefaultExceptionProcessor(); @Autowired(required = false) public void setProcessor(List processors) { // never be null, "orElse" just to avoid compile warning processor = processors.stream() .min(Comparator.comparingInt(ExceptionProcessor::getOrder)) .orElse(new DefaultExceptionProcessor()); } public static Throwable unwrapIncludeInvocationException(Throwable throwable) { return ExceptionFactory.unwrapIncludeInvocationException(throwable); } public static T unwrap(Throwable throwable) { return ExceptionFactory.unwrap(throwable); } public static InvocationException create(StatusType status, Object errorData) { return new InvocationException(status, errorData); } public static InvocationException create(StatusType status, String code, String msg) { return new InvocationException(status, code, msg); } private static InvocationException create(StatusType status, String code, String msg, Throwable cause) { return new InvocationException(status, code, msg, cause); } public static InvocationException consumer(String code, String msg) { return create(BAD_REQUEST, code, msg); } public static InvocationException consumer(String code, String msg, Throwable cause) { if (cause instanceof InvocationException) { return (InvocationException) cause; } return create(BAD_REQUEST, code, msg, cause); } public static InvocationException genericConsumer(String msg) { return consumer(GENERIC_CLIENT, msg); } public static InvocationException genericConsumer(String msg, Throwable cause) { return consumer(GENERIC_CLIENT, msg, cause); } public static InvocationException producer(String code, String msg) { return create(INTERNAL_SERVER_ERROR, code, msg); } public static InvocationException producer(String code, String msg, Throwable cause) { return create(INTERNAL_SERVER_ERROR, code, msg, cause); } public static InvocationException genericProducer(String msg) { return producer(GENERIC_SERVER, msg); } public static InvocationException genericProducer(String msg, Throwable cause) { return producer(GENERIC_SERVER, msg, cause); } public static StatusType getGenericStatus(Invocation invocation) { return CONSUMER.equals(invocation.getInvocationType()) ? BAD_REQUEST : INTERNAL_SERVER_ERROR; } public static Response toConsumerResponse(Invocation invocation, Throwable throwable) { return processor.toConsumerResponse(invocation, throwable); } public static Response toProducerResponse(Invocation invocation, Throwable exception) { return processor.toProducerResponse(invocation, exception); } public static InvocationException convert(Invocation invocation, Throwable throwable) { return processor.convert(invocation, throwable); } public static InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { return processor.convert(invocation, throwable, genericStatus); } public static boolean isPrintInvocationStackTrace() { return processor.isPrintStackTrace(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/ConnectTimeoutExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ConnectTimeoutException; import jakarta.ws.rs.core.Response.StatusType; public class ConnectTimeoutExceptionConverter implements ExceptionConverter { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectTimeoutExceptionConverter.class); public static final int ORDER = Short.MAX_VALUE; @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return throwable instanceof ConnectTimeoutException; } @Override public InvocationException convert(Invocation invocation, ConnectTimeoutException throwable, StatusType genericStatus) { // throwable.getMessage: // connection timed out: /1.1.1.1:8080 // should not copy the message to invocationException to avoid leak server ip address LOGGER.info("connection timed out, Details: {}.", throwable.getMessage()); return new InvocationException(INTERNAL_SERVER_ERROR, ExceptionConverter.getGenericCode(genericStatus), "connection timed out.", throwable); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/ConstraintViolationExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.servicecomb.core.exception.ExceptionCodes.DEFAULT_VALIDATE; import java.util.List; import java.util.stream.Collectors; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.validation.ConstraintViolationException; import jakarta.ws.rs.core.Response.StatusType; public class ConstraintViolationExceptionConverter implements ExceptionConverter { public static final int ORDER = Short.MAX_VALUE; public static final String KEY_CODE = "servicecomb.filters.validate.code"; public ConstraintViolationExceptionConverter() { } @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return throwable instanceof ConstraintViolationException; } @Override public InvocationException convert(Invocation invocation, ConstraintViolationException throwable, StatusType genericStatus) { List details = throwable.getConstraintViolations().stream() .map(violation -> new ValidateDetail(violation.getPropertyPath().toString(), violation.getMessage())) .collect(Collectors.toList()); CommonExceptionData exceptionData = new CommonExceptionData(SCBEngine.getInstance().getEnvironment(). getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE), "invalid parameters."); exceptionData.putDynamic("validateDetail", details); return new InvocationException(BAD_REQUEST, exceptionData); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/DefaultExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.StatusType; /** *
 *   Only very few exceptions carry sensitive data
 *   If discard all exception messages for these very few exceptions, it will cause difficult to locate problems
 *
 *   so default to remain the messages, and log the exception
 *
 *   if want to change these:
 *   1. customize a new converter
 *   2. disabled log for this class by log configuration
 * 
*/ public class DefaultExceptionConverter implements ExceptionConverter { public static final int ORDER = Integer.MAX_VALUE; @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return true; } @Override public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { String msg = String.format("Unexpected exception when processing %s. %s", invocation == null ? "none" : invocation.getMicroserviceQualifiedName(), throwable.getMessage()); return new InvocationException(genericStatus, ExceptionConverter.getGenericCode(genericStatus), msg, throwable); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/IllegalArgumentExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.StatusType; public class IllegalArgumentExceptionConverter implements ExceptionConverter { public static final int ORDER = Short.MAX_VALUE; @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return throwable instanceof IllegalArgumentException; } @Override public InvocationException convert(Invocation invocation, IllegalArgumentException throwable, StatusType genericStatus) { return new InvocationException(genericStatus, ExceptionConverter.getGenericCode(genericStatus), "Parameters not valid or types not match.", throwable); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/InvocationExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.StatusType; public class InvocationExceptionConverter implements ExceptionConverter { public static final int ORDER = Byte.MAX_VALUE; @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return throwable instanceof InvocationException; } @Override public InvocationException convert(Invocation invocation, InvocationException throwable, StatusType genericStatus) { return throwable; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/ServiceCombExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import java.util.concurrent.TimeoutException; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; public class ServiceCombExceptionConverter implements ExceptionConverter { public static final int ORDER = Byte.MAX_VALUE; private TimeoutExceptionConverter timeoutExceptionConverter = new TimeoutExceptionConverter(); @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return throwable instanceof ServiceCombException; } @Override public InvocationException convert(Invocation invocation, ServiceCombException throwable, StatusType genericStatus) { if (throwable.getCause() instanceof TimeoutException) { return timeoutExceptionConverter.convert(invocation, (TimeoutException) throwable.getCause(), genericStatus); } return new InvocationException(Status.INTERNAL_SERVER_ERROR, ExceptionConverter.getGenericCode(genericStatus), throwable.getMessage(), throwable); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; import java.util.concurrent.TimeoutException; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jakarta.ws.rs.core.Response.StatusType; public class TimeoutExceptionConverter implements ExceptionConverter { private static final Logger LOGGER = LoggerFactory.getLogger(TimeoutExceptionConverter.class); public static final int ORDER = Short.MAX_VALUE; @Override public int getOrder() { return ORDER; } @Override public boolean canConvert(Throwable throwable) { return throwable instanceof TimeoutException; } @Override public InvocationException convert(Invocation invocation, TimeoutException throwable, StatusType genericStatus) { // throwable.getMessage: // The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080 // should not copy the message to invocationException to avoid leak server ip address LOGGER.info("Request timeout, Details: {}.", throwable.getMessage()); return new InvocationException(REQUEST_TIMEOUT, ExceptionConverter.getGenericCode(genericStatus), "Request Timeout.", throwable); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/exception/converter/ValidateDetail.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; public class ValidateDetail { private String propertyPath; private String message; public ValidateDetail() { } public ValidateDetail(String propertyPath, String message) { this.propertyPath = propertyPath; this.message = message; } public String getPropertyPath() { return propertyPath; } public ValidateDetail setPropertyPath(String propertyPath) { this.propertyPath = propertyPath; return this; } public String getMessage() { return message; } public ValidateDetail setMessage(String message) { this.message = message; return this; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.util.Map; import java.util.concurrent.Executor; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public final class ExecutorManager { public static final String KEY_EXECUTORS_PREFIX = "servicecomb.executors.Provider."; public static final String KEY_EXECUTORS_DEFAULT = "servicecomb.executors.default"; public static final String EXECUTOR_GROUP_THREAD_POOL = "servicecomb.executor.groupThreadPool"; public static final String EXECUTOR_REACTIVE = "servicecomb.executor.reactive"; private final Map executors = new ConcurrentHashMapEx<>(); static String EXECUTOR_DEFAULT = EXECUTOR_GROUP_THREAD_POOL; public static void setExecutorDefault(String executorDefault) { EXECUTOR_DEFAULT = executorDefault; } private Environment environment; public ExecutorManager() { registerExecutor(EXECUTOR_REACTIVE, new ReactiveExecutor()); } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } public void registerExecutor(String id, Executor executor) { Executor existing = executors.putIfAbsent(id, executor); if (existing != null) { throw new IllegalStateException(String.format( "duplicated executor, id=%s, old executor=%s, new executor=%s", id, existing.getClass().getName(), executor.getClass().getName())); } } // 只会在初始化时执行,一点点重复的查找,没必要做缓存 public Executor findExecutor(OperationMeta operationMeta) { return findExecutor(operationMeta, null); } public Executor findExecutor(OperationMeta operationMeta, Executor defaultOperationExecutor) { // operation级别 Executor executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getMicroserviceQualifiedName()); if (executor != null) { return executor; } executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getSchemaQualifiedName()); if (executor != null) { return executor; } if (defaultOperationExecutor != null) { return defaultOperationExecutor; } // schema级别 executor = findByKey( KEY_EXECUTORS_PREFIX + operationMeta.getMicroserviceName() + '.' + operationMeta.getSchemaId()); if (executor != null) { return executor; } executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getSchemaId()); if (executor != null) { return executor; } // microservice级别 executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getMicroserviceName()); if (executor != null) { return executor; } // 寻找config-key指定的default executor = findByKey(KEY_EXECUTORS_DEFAULT); if (executor != null) { return executor; } return findExecutorById(EXECUTOR_DEFAULT); } private Executor findByKey(String configKey) { String id = environment.getProperty(configKey); if (id == null) { return null; } return findExecutorById(id); } public Executor findExecutorById(String id) { Executor executor = executors.get(id); if (executor != null) { return executor; } return BeanUtils.getBean(id); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.io.Closeable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; public class GroupExecutor implements Executor, Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(GroupExecutor.class); public static final String KEY_GROUP = "servicecomb.executor.default.group"; // Deprecated public static final String KEY_OLD_MAX_THREAD = "servicecomb.executor.default.thread-per-group"; public static final String KEY_CORE_THREADS = "servicecomb.executor.default.coreThreads-per-group"; public static final String KEY_MAX_THREADS = "servicecomb.executor.default.maxThreads-per-group"; public static final String KEY_MAX_IDLE_SECOND = "servicecomb.executor.default.maxIdleSecond-per-group"; public static final String KEY_MAX_QUEUE_SIZE = "servicecomb.executor.default.maxQueueSize-per-group"; private static final AtomicBoolean LOG_PRINTED = new AtomicBoolean(); private final Environment environment; protected String groupName; protected int groupCount; protected int coreThreads; protected int maxThreads; protected int maxIdleInSecond; protected int maxQueueSize; // to avoid multiple network thread conflicted when put tasks to executor queue private final List executorList = new ArrayList<>(); // for bind network thread to one executor // it's impossible that has too many network thread, so index will not too big that less than 0 private final AtomicInteger index = new AtomicInteger(); private final Map threadExecutorMap = new ConcurrentHashMapEx<>(); public GroupExecutor(Environment environment) { this.environment = environment; } public GroupExecutor init() { return init("group"); } public GroupExecutor init(String groupName) { this.groupName = groupName; initConfig(); for (int groupIdx = 0; groupIdx < groupCount; groupIdx++) { GroupThreadFactory factory = new GroupThreadFactory(groupName + groupIdx); ThreadPoolExecutorEx executor = new ThreadPoolExecutorEx(coreThreads, maxThreads, maxIdleInSecond, TimeUnit.SECONDS, new LinkedBlockingQueueEx(maxQueueSize), factory); executorList.add(executor); } return this; } public synchronized void initConfig() { if (LOG_PRINTED.compareAndSet(false, true)) { LOGGER.info("thread pool rules:\n" + "1.use core threads.\n" + "2.if all core threads are busy, then create new thread.\n" + "3.if thread count reach the max limitation, then queue the request.\n" + "4.if queue is full, and threads count is max, then reject the request."); } groupCount = environment.getProperty(KEY_GROUP, int.class, 2); coreThreads = environment.getProperty(KEY_CORE_THREADS, int.class, 25); maxThreads = environment.getProperty(KEY_MAX_THREADS, int.class, -1); if (maxThreads <= 0) { maxThreads = environment.getProperty(KEY_OLD_MAX_THREAD, int.class, -1); if (maxThreads > 0) { LOGGER.warn("{} is deprecated, recommended to use {}.", KEY_OLD_MAX_THREAD, KEY_MAX_THREADS); } else { maxThreads = 100; } } if (coreThreads > maxThreads) { LOGGER.warn("coreThreads is bigger than maxThreads, change from {} to {}.", coreThreads, maxThreads); coreThreads = maxThreads; } maxIdleInSecond = environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60); maxQueueSize = environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE); LOGGER.info( "executor name={}, group={}. per group settings, coreThreads={}, maxThreads={}, maxIdleInSecond={}, maxQueueSize={}.", groupName, groupCount, coreThreads, maxThreads, maxIdleInSecond, maxQueueSize); } public List getExecutorList() { return executorList; } @Override public void execute(Runnable command) { long threadId = Thread.currentThread().getId(); Executor executor = threadExecutorMap.computeIfAbsent(threadId, this::chooseExecutor); executor.execute(command); } private Executor chooseExecutor(long threadId) { int idx = index.getAndIncrement() % executorList.size(); return executorList.get(idx); } @Override public void close() { for (ExecutorService executorService : executorList) { executorService.shutdown(); } executorList.clear(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/executor/GroupThreadFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; public class GroupThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; public GroupThreadFactory(String prefix) { Objects.requireNonNull(prefix); group = Thread.currentThread().getThreadGroup(); namePrefix = prefix + "-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) { t.setDaemon(false); } if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/executor/LinkedBlockingQueueEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; public class LinkedBlockingQueueEx extends LinkedBlockingQueue { private static final long serialVersionUID = -1L; private transient volatile ThreadPoolExecutorEx owner = null; public LinkedBlockingQueueEx(int capacity) { super(capacity); } public void setOwner(ThreadPoolExecutorEx owner) { this.owner = owner; } @Override public boolean offer(Runnable runnable) { // task can come before owner available if (owner == null) { return super.offer(runnable); } // can not create more thread, just queue the task if (owner.getPoolSize() == owner.getMaximumPoolSize()) { return super.offer(runnable); } // no need to create more thread, just queue the task if (owner.getNotFinished() <= owner.getPoolSize()) { return super.offer(runnable); } // all threads are busy, and can create new thread, not queue the task if (owner.getPoolSize() < owner.getMaximumPoolSize()) { return false; } return super.offer(runnable); } /* * when task is rejected (thread pool if full), force the item onto queue. */ public boolean force(Runnable runnable) { if (owner == null || owner.isShutdown()) { throw new RejectedExecutionException("queue is not running."); } return super.offer(runnable); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/executor/ReactiveExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.io.Closeable; import java.util.concurrent.Executor; /** * 用于在verticle中就地执行,不做多余的调度,这是性能最高的一种模型 */ public class ReactiveExecutor implements Executor, Closeable { @Override public void execute(Runnable command) { command.run(); } @Override public void close() { } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/executor/ThreadPoolExecutorEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.util.concurrent.BlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class ThreadPoolExecutorEx extends ThreadPoolExecutor { private final AtomicInteger submittedCount = new AtomicInteger(); private final AtomicInteger finishedCount = new AtomicInteger(); private final AtomicInteger rejectedCount = new AtomicInteger(); public ThreadPoolExecutorEx(int coreThreads, int maxThreads, int maxIdleInSecond, TimeUnit timeUnit, BlockingQueue queue, ThreadFactory threadFactory) { super(coreThreads, maxThreads, maxIdleInSecond, timeUnit, queue, threadFactory); if (queue instanceof LinkedBlockingQueueEx) { ((LinkedBlockingQueueEx) queue).setOwner(this); } setRejectedExecutionHandler(this::rejectedExecution); } @Override public void execute(Runnable command) { submittedCount.incrementAndGet(); try { super.execute(command); } catch (RejectedExecutionException e) { if (getQueue() instanceof LinkedBlockingQueueEx) { final LinkedBlockingQueueEx queue = (LinkedBlockingQueueEx) getQueue(); if (!queue.force(command)) { throw new RejectedExecutionException("thread pool queue is full"); } } else { throw e; } } } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { rejectedCount.incrementAndGet(); finishedCount.incrementAndGet(); throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); finishedCount.incrementAndGet(); } public int getNotFinished() { return submittedCount.get() - finishedCount.get(); } public int getRejectedCount() { return rejectedCount.get(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public abstract class AbstractFilter implements Filter, EnvironmentAware { private static final String ORDER_KEY = "servicecomb.filter.%s.%s.%s.order"; private static final String ENABLE_KEY = "servicecomb.filter.%s.%s.%s.enabled"; protected Environment environment; private String nameWithOrder; @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public int getOrder(String application, String serviceName) { Integer custom = environment.getProperty(String.format(ORDER_KEY, getName(), application, serviceName), Integer.class); if (custom != null) { return custom; } return getOrder(); } @Override public boolean enabledForMicroservice(String application, String serviceName) { Boolean custom = environment.getProperty(String.format(ENABLE_KEY, getName(), application, serviceName), Boolean.class); if (custom != null) { return custom; } return true; } @Override public String getNameWithOrder() { if (nameWithOrder == null) { nameWithOrder = String.format("F(%1$06d)-%2$s", getOrder(), getName()); } return nameWithOrder; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; public interface ConsumerFilter extends Filter { } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import org.apache.servicecomb.core.filter.impl.ContextMapperFilter; import org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter; import org.apache.servicecomb.core.filter.impl.ProviderOperationFilter; import org.apache.servicecomb.core.filter.impl.RetryFilter; import org.apache.servicecomb.core.filter.impl.ScheduleFilter; import org.apache.servicecomb.governance.handler.MapperHandler; import org.apache.servicecomb.governance.handler.RetryHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CoreFilterConfiguration { @Bean public ProviderOperationFilter scbProducerOperationFilter() { return new ProviderOperationFilter(); } @Bean public ScheduleFilter scbScheduleFilter() { return new ScheduleFilter(); } @Bean public RetryFilter scbRetryFilter(RetryHandler retryHandler) { return new RetryFilter(retryHandler); } @Bean public ContextMapperFilter scbContextMapperFilter(MapperHandler mapperHandler) { return new ContextMapperFilter(mapperHandler); } @Bean public FilterChainsManager scbFilterChainsManager() { return new FilterChainsManager(); } @Bean public ParameterValidatorFilter scbParameterValidatorFilter() { return new ParameterValidatorFilter(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; public interface EdgeFilter extends Filter { } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/Filter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.core.Ordered; /** *
 *  Filters are the basics of how an invocation is executed.
 *
 * thread rule:
 *   assume a provider filter chains is: f1, f2, schedule, f3, f4
 *
 *   schedule is a builtIn filter, which will dispatch invocations to operation related threadPool
 *
 *   f1 and f2 are before "schedule" filter, default to run in eventLoop
 *   if developers customize filters and switch to other threads
 *   it's better to switch back to eventLoop, unless you know what you are doing
 *
 *   f3 and f4 are after "schedule" filter, default thread depend on controller's method signature
 *     1. if controller method not return CompletableFuture
 *        then will run in a real threadPool
 *     2. if controller method return CompletableFuture
 *        then will still run in eventLoop
 *   so filters after "schedule" filter, are more complex than filters before "schedule"
 *   if developers need to do some BLOCK logic, MUST use different Strategy when running in different thread:
 *     1. threadPool: run do BLOCK logic directly
 *     2. eventLoop: MUST submit to a threadPool, and then switch back
 *        (reactive golden rule)
 * 
*/ public interface Filter extends Ordered { int PROVIDER_SCHEDULE_FILTER_ORDER = 0; int CONSUMER_LOAD_BALANCE_ORDER = 0; default boolean enabledForTransport(String transport) { return true; } default boolean enabledForMicroservice(String application, String serviceName) { return true; } default int getOrder(String application, String serviceName) { return 0; } default int getOrder() { return 0; } default String getName() { throw new IllegalStateException("must provide unique filter name."); } String getNameWithOrder(); /** * * @param invocation invocation * @param nextNode node filter node * @return response future
* even Response can express fail data
* but Response only express success data in filter chain
* all fail data can only express by exception
*
* special for producer:
* if response is failure, then after encode response, response.result will * be exception.errorData, not a exception */ CompletableFuture onFilter(Invocation invocation, FilterNode nextNode); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.deleteLast; import java.util.List; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; public class FilterChainsManager { private InvocationFilterChains consumerChains; private InvocationFilterChains providerChains; private InvocationFilterChains edgeChains; @Autowired public FilterChainsManager setEdgeFilters(List filters) { edgeChains = new InvocationFilterChains(filters); return this; } @Autowired public FilterChainsManager setConsumerFilters(List filters) { consumerChains = new InvocationFilterChains(filters); return this; } @Autowired public FilterChainsManager setProviderFilters(List filters) { providerChains = new InvocationFilterChains(filters); return this; } public FilterChainsManager init() { return this; } public FilterNode findConsumerChain(String application, String serviceName) { return consumerChains.findChain(application, serviceName); } public FilterNode findProducerChain(String application, String serviceName) { return providerChains.findChain(application, serviceName); } public FilterNode findEdgeChain(String application, String serviceName) { return edgeChains.findChain(application, serviceName); } public String collectResolvedChains() { StringBuilder sb = new StringBuilder(); appendLine(sb, "consumer: "); appendLine(sb, " filters: %s", collectFilterNames(consumerChains)); appendLine(sb, "producer: "); appendLine(sb, " filters: %s", collectFilterNames(providerChains)); appendLine(sb, "edge: "); appendLine(sb, " filters: %s", collectFilterNames(edgeChains)); return deleteLast(sb, 1).toString(); } private List collectFilterNames(InvocationFilterChains chains) { return chains.getFilters().stream() .map(filter -> filter.getName() + "(" + filter.getOrder() + ")") .collect(Collectors.toList()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.invocation.Response; public class FilterNode { public static final FilterNode EMPTY = new FilterNode(null) { @Override public CompletableFuture onFilter(Invocation invocation) { return CompletableFuture.completedFuture(Response.ok(null)); } }; public static FilterNode buildChain(Filter... filters) { return buildChain(Arrays.asList(filters)); } public static FilterNode buildChain(List filters) { List filterNodes = filters.stream() .map(FilterNode::new).toList(); for (int idx = 0; idx < filterNodes.size() - 1; idx++) { FilterNode currentNode = filterNodes.get(idx); FilterNode nextNode = filterNodes.get(idx + 1); currentNode.setNextNode(nextNode); } return filterNodes.get(0); } private final Filter filter; private FilterNode nextNode; public FilterNode(Filter filter) { this.filter = filter; } private void setNextNode(FilterNode nextNode) { this.nextNode = nextNode; } public CompletableFuture onFilter(Invocation invocation) { // When transport name is empty, maybe edge transport filters need to be executed. // Can't set Endpoint before load balance in edge. if (invocation.getTransportName() != null && !filter.enabledForTransport(invocation.getTransportName())) { return nextNode.onFilter(invocation); } String stage = invocation.getInvocationStageTrace().recordStageBegin(this.filter.getNameWithOrder()); return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode) .whenComplete((r, e) -> invocation.getInvocationStageTrace().recordStageEnd(stage))); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class InvocationFilterChains { private final List filters; private final Map> microserviceChains = new ConcurrentHashMapEx<>(); public InvocationFilterChains(List filters) { this.filters = filters; } public List getFilters() { return filters; } public FilterNode findChain(String application, String serviceName) { return microserviceChains.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()) .computeIfAbsent(serviceName, (serviceNameInner) -> { List serviceFilters = filters.stream() .filter(e -> e.enabledForMicroservice(application, serviceName)) .sorted(Comparator.comparingInt(a -> a.getOrder(application, serviceName))) .collect(Collectors.toList()); return FilterNode.buildChain(serviceFilters); }); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; public interface ProviderFilter extends Filter { } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.MapperHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.processor.mapping.Mapper; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.util.CollectionUtils; public class ContextMapperFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private final MapperHandler mapperHandler; public ContextMapperFilter(MapperHandler mapperHandler) { this.mapperHandler = mapperHandler; } @Override public boolean enabledForTransport(String transport) { return CoreConst.RESTFUL.equals(transport); } @Override public int getOrder() { return -1995; } @Override public String getName() { return "context-mapper"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); Mapper mapper = mapperHandler.getActuator(request); if (mapper == null || CollectionUtils.isEmpty(mapper.target())) { return nextNode.onFilter(invocation); } Map properties = mapper.target(); properties.forEach((k, v) -> { if (StringUtils.isEmpty(v)) { return; } if ("$U".equals(v)) { invocation.addContext(k, request.apiPath()); } else if ("$M".equals(v)) { invocation.addContext(k, request.method()); } else if (v.startsWith("$H{") && v.endsWith("}")) { invocation.addContext(k, request.header(v.substring(3, v.length() - 1))); } else if (v.startsWith("$Q{") && v.endsWith("}")) { invocation.addContext(k, request.query(v.substring(3, v.length() - 1))); } else { invocation.addContext(k, v); } }); return nextNode.onFilter(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; // just for test public class EmptyFilter extends AbstractFilter implements ProviderFilter, ConsumerFilter, EdgeFilter { @Override public String getName() { return "empty"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return CompletableFuture.completedFuture(Response.ok(null)); } @Override public int getOrder() { return 0; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/JacksonPropertyNodeNameProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; import org.hibernate.validator.spi.nodenameprovider.Property; import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import io.vertx.core.json.jackson.DatabindCodec; /** * hibernate validator will cache the resolved data
* no need to worry about performance problem */ public class JacksonPropertyNodeNameProvider implements PropertyNodeNameProvider { @Override public String getName(Property property) { if (property instanceof JavaBeanProperty) { return getJavaBeanPropertyName((JavaBeanProperty) property); } return property.getName(); } private String getJavaBeanPropertyName(JavaBeanProperty property) { ObjectMapper objectMapper = DatabindCodec.mapper(); JavaType type = objectMapper.constructType(property.getDeclaringClass()); BeanDescription desc = objectMapper.getSerializationConfig().introspect(type); return desc.findProperties() .stream() .filter(prop -> prop.getInternalName().equals(property.getName())) .map(BeanPropertyDefinition::getName) .findFirst() .orElse(property.getName()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.invocation.Response; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import jakarta.validation.Configuration; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Valid; import jakarta.validation.Validation; import jakarta.validation.ValidatorFactory; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.executable.ExecutableValidator; import jakarta.validation.groups.Default; public class ParameterValidatorFilter extends AbstractFilter implements ProviderFilter, InitializingBean { private static class Service { @SuppressWarnings("unused") public void service(@Valid Model model) { } } private static class Model { @NotNull String name; @Min(10) int age; Model(String name, int age) { this.name = name; this.age = age; } } private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidatorFilter.class); public static final String NAME = "validator"; public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator"; public static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator"; protected ExecutableValidator validator; @Override public String getName() { return NAME; } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1000; } @Override public void afterPropertiesSet() { validator = createValidatorFactory() .getValidator().forExecutables(); initValidate(); } private void initValidate() { // This method is intended to make first rpc call faster // Because validation cache bean class, this way only make first rpc call a little faster. try { Model model = new Model("foo", 23); Service instance = new Service(); Method method = Service.class.getMethod("service", Model.class); Object[] args = new Object[] {model}; validator.validateParameters(instance, method, args, Default.class); } catch (Throwable e) { throw new IllegalStateException(e); } } protected ValidatorFactory createValidatorFactory() { Configuration validatorConfiguration = Validation.byProvider(HibernateValidator.class) .configure() .propertyNodeNameProvider(new JacksonPropertyNodeNameProvider()) .messageInterpolator(messageInterpolator()); Map properties = ConfigUtil.stringPropertiesWithPrefix(environment, HIBERNATE_VALIDATE_PREFIX); if (!properties.isEmpty()) { for (Map.Entry entry : properties.entrySet()) { validatorConfiguration.addProperty(entry.getKey(), entry.getValue()); } } return validatorConfiguration.buildValidatorFactory(); } protected AbstractMessageInterpolator messageInterpolator() { if (useResourceBundleMessageInterpolator()) { return new ResourceBundleMessageInterpolator(); } return new ParameterMessageInterpolator(); } private boolean useResourceBundleMessageInterpolator() { return environment.getProperty(ENABLE_EL, boolean.class, false); } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Set> violations = doValidate(invocation); if (violations.size() > 0) { LOGGER.error("Parameter validation failed : " + violations); return AsyncUtils.completeExceptionally(new ConstraintViolationException(violations)); } return nextNode.onFilter(invocation); } protected Set> doValidate(Invocation invocation) { SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); return validator.validateParameters(producerOperation.getProducerInstance(), producerOperation.getProducerMethod(), invocation.toProducerArguments(), Default.class); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import java.lang.reflect.Method; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.Status; public class ProviderOperationFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "producer-operation"; @Override public String getName() { return NAME; } @Override public int getOrder() { // almost time, should be the last filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 2000; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (!transportAccessAllowed(invocation)) { return CompletableFuture.failedFuture(new InvocationException(Status.UNAUTHORIZED, new CommonExceptionData("transport access not allowed."))); } invocation.onBusinessMethodStart(); SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); Object instance = producerOperation.getProducerInstance(); Method method = producerOperation.getProducerMethod(); Object[] args = invocation.toProducerArguments(); return invoke(invocation, instance, method, args) .thenApply(result -> convertResultToResponse(invocation, producerOperation, result)) .whenComplete((response, throwable) -> processMetrics(invocation)); } private boolean transportAccessAllowed(Invocation invocation) { if (invocation.getProviderTransportName() == null) { return true; } return invocation.getProviderTransportName().equals(invocation.getTransportName()); } @SuppressWarnings("unchecked") protected CompletableFuture invoke(Invocation invocation, Object instance, Method method, Object[] args) { ContextUtils.setInvocationContext(invocation); try { Object result = method.invoke(instance, args); if (result instanceof CompletableFuture) { return (CompletableFuture) result; } return CompletableFuture.completedFuture(result); } catch (Throwable e) { return AsyncUtils.completeExceptionally(Exceptions.unwrap(e)); } finally { ContextUtils.removeInvocationContext(); } } protected Response convertResultToResponse(Invocation invocation, SwaggerProducerOperation producerOperation, Object result) { return producerOperation.getResponseMapper().mapResponse(invocation.getStatus(), result); } protected void processMetrics(Invocation invocation) { invocation.onBusinessFinish(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.GovernanceConfiguration; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.core.governance.RetryContext; import org.apache.servicecomb.governance.handler.RetryHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.beans.factory.annotation.Autowired; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; import io.github.resilience4j.retry.Retry; public class RetryFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Object LOCK = new Object(); private static volatile ScheduledExecutorService reactiveRetryPool; private static ScheduledExecutorService getOrCreateRetryPool() { if (reactiveRetryPool == null) { synchronized (LOCK) { if (reactiveRetryPool == null) { reactiveRetryPool = Executors.newScheduledThreadPool(2, new ThreadFactory() { private final AtomicInteger count = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "reactive-retry-pool-thread-" + count.getAndIncrement()); // avoid block shutdown thread.setDaemon(true); return thread; } }); } } } return reactiveRetryPool; } private final RetryHandler retryHandler; @Autowired public RetryFilter(RetryHandler retryHandler) { this.retryHandler = retryHandler; } @Override public String getName() { return "retry"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); Retry retry = retryHandler.getActuator(request); if (retry == null) { return nextNode.onFilter(invocation); } Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); dcs.withRetry(retry, getOrCreateRetryPool()); CompletableFuture future = new CompletableFuture<>(); dcs.get().whenComplete((r, e) -> { if (e == null) { future.complete(r); return; } future.completeExceptionally(e); }); return future; } private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, FilterNode nextNode) { return () -> { updateRetryStatus(invocation); return nextNode.onFilter(invocation); }; } private static void updateRetryStatus(Invocation invocation) { if (invocation.getLocalContext(RetryContext.RETRY_CONTEXT) != null) { if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null && (boolean) invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) { // clear last server to avoid using user defined endpoint invocation.setEndpoint(null); } RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); retryContext.incrementRetry(); return; } invocation.addLocalContext(RetryContext.RETRY_CONTEXT, new RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()))); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.MDC; import jakarta.ws.rs.core.Response.Status; public class ScheduleFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "schedule"; @Override public String getName() { return NAME; } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode next) { invocation.getInvocationStageTrace().startProviderQueue(); Executor executor = invocation.getOperationMeta().getExecutor(); return CompletableFuture.completedFuture(null) .thenComposeAsync(response -> runInExecutor(invocation, next), executor); } protected CompletableFuture runInExecutor(Invocation invocation, FilterNode next) { invocation.getInvocationStageTrace().finishProviderQueue(); MDC.put(TraceIdLogger.KEY_TRACE_ID, invocation.getTraceId()); checkInQueueTimeout(invocation); return next.onFilter(invocation); } private void checkInQueueTimeout(Invocation invocation) { long nanoTimeout = invocation.getOperationMeta().getConfig() .getNanoRequestWaitInPoolTimeout(invocation.getTransport().getName()); if (invocation.getInvocationStageTrace().calcQueue() > nanoTimeout) { throw new InvocationException(Status.REQUEST_TIMEOUT, "Request in the queue timed out."); } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/CoreGovernanceConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import org.apache.servicecomb.core.SCBEngine; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CoreGovernanceConfiguration { @Bean public ServiceCombCircuitBreakerExtension serviceCombCircuitBreakerExtension() { return new ServiceCombCircuitBreakerExtension(); } @Bean public ServiceCombInstanceIsolationExtension serviceCombInstanceIsolationExtension() { return new ServiceCombInstanceIsolationExtension(); } @Bean public ServiceCombMicroserviceMeta serviceCombMicroserviceMeta(SCBEngine scbEngine) { return new ServiceCombMicroserviceMeta(scbEngine); } @Bean public ServiceCombRetryExtension serviceCombRetryExtension() { return new ServiceCombRetryExtension(); } @Bean public ServiceCombConfigurationEventAdapter serviceCombConfigurationEventAdapter() { return new ServiceCombConfigurationEventAdapter(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/GovernanceConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public class GovernanceConfiguration { public static final String ROOT = "servicecomb.loadbalance."; // retry configurations public static final String RETRY_ENABLED = "retryEnabled"; public static final String RETRY_ON_NEXT = "retryOnNext"; public static final String RETRY_ON_SAME = "retryOnSame"; public static boolean isRetryEnabled(String microservice) { String p = getStringProperty("false", ROOT + microservice + "." + RETRY_ENABLED, ROOT + RETRY_ENABLED); return Boolean.parseBoolean(p); } public static int getRetryNextServer(String microservice) { return getRetryServer(microservice, RETRY_ON_NEXT); } public static int getRetrySameServer(String microservice) { return getRetryServer(microservice, RETRY_ON_SAME); } private static int getRetryServer(String microservice, String retryType) { final int defaultValue = 0; String p = getStringProperty("0", ROOT + microservice + "." + retryType, ROOT + retryType); try { int result = Integer.parseInt(p); if (result > 0) { return result; } return defaultValue; } catch (NumberFormatException e) { return defaultValue; } } public static String getStringProperty(String defaultValue, String... keys) { String property; for (String key : keys) { property = LegacyPropertyFactory.getStringProperty(key); if (property != null) { return property; } } return defaultValue; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import java.util.Map; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.SwaggerUtils; public final class MatchType { private static class GovernanceRequestExtractorImpl implements GovernanceRequestExtractor { private final Invocation invocation; private GovernanceRequestExtractorImpl(Invocation invocation) { this.invocation = invocation; } @Override public String apiPath() { if (MatchType.REST.equalsIgnoreCase(invocation.getOperationMeta().getConfig().getGovernanceMatchType())) { if (!invocation.isProducer()) { return SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), invocation.getOperationMeta().getOperationPath()); } // not highway if (invocation.getRequestEx() != null) { return invocation.getRequestEx().getRequestURI(); } } if (!invocation.isProducer()) { return invocation.getOperationMeta().getMicroserviceQualifiedName(); } return invocation.getOperationMeta().getSchemaQualifiedName(); } @Override public String method() { return invocation.getOperationMeta().getHttpMethod(); } @Override public String header(String key) { Map arguments = invocation.getSwaggerArguments(); if (arguments != null && arguments.get(key) != null) { return arguments.get(key).toString(); } if (invocation.getContext(key) != null) { return invocation.getContext(key); } if (invocation.getRequestEx() != null) { return invocation.getRequestEx().getHeader(key); } return null; } @Override public String query(String key) { HttpServletRequestEx requestEx = invocation.getRequestEx(); if (requestEx == null) { return null; } return requestEx.getParameter(key); } @Override public String instanceId() { if (!invocation.isProducer()) { if (invocation.getEndpoint() != null && invocation.getEndpoint().getMicroserviceInstance() != null) { return invocation.getEndpoint().getMicroserviceInstance().getInstanceId(); } } return null; } @Override public String serviceName() { if (!invocation.isProducer()) { return invocation.getMicroserviceName(); } return null; } @Override public Object sourceRequest() { return invocation; } } public static final String REST = "rest"; public static final String RPC = "rpc"; public static GovernanceRequestExtractor createGovHttpRequest(Invocation invocation) { return new GovernanceRequestExtractorImpl(invocation); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; public class RetryContext { public static final String RETRY_CONTEXT = "x-context-retry"; // weather need reset Endpoint in retry public static final String RETRY_LOAD_BALANCE = "x-context-retry-loadbalance"; private boolean retry; private int triedCount; private final int retryOnSame; public RetryContext(int retryOnSame) { this.retryOnSame = retryOnSame; this.retry = false; this.triedCount = 0; } public boolean isRetry() { return retry; } public void incrementRetry() { this.retry = true; this.triedCount++; } public boolean trySameServer() { return triedCount <= retryOnSame; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public class ServiceCombCircuitBreakerExtension extends AbstractCircuitBreakerExtension { @Override protected String extractStatusCode(Object result) { if (!(result instanceof Response)) { return null; } Response resp = (Response) result; if (resp.isFailed()) { if (resp.getResult() instanceof InvocationException) { InvocationException e = resp.getResult(); return String.valueOf(e.getStatusCode()); } } return String.valueOf(resp.getStatusCode()); } @Override public boolean isFailedResult(List statusList, Throwable e) { if (e instanceof InvocationException) { InvocationException invocationException = (InvocationException) e; return statusList.contains(String.valueOf(invocationException.getStatusCode())); } return super.isFailedResult(statusList, e); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombConfigurationEventAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import com.google.common.eventbus.Subscribe; public class ServiceCombConfigurationEventAdapter { public ServiceCombConfigurationEventAdapter() { EventManager.register(this); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(event.getChanged()); GovernanceEventManager.post(newEvent); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; public class ServiceCombInstanceIsolationExtension extends AbstractInstanceIsolationExtension { private final ServiceCombRetryExtension retryExtension = new ServiceCombRetryExtension(); @Override protected String extractStatusCode(Object result) { return retryExtension.extractStatusCode(result); } @Override public boolean isFailedResult(List statusList, Throwable e) { return retryExtension.isFailedResult(statusList, e); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombMicroserviceMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.governance.MicroserviceMeta; public class ServiceCombMicroserviceMeta implements MicroserviceMeta { private SCBEngine scbEngine; public ServiceCombMicroserviceMeta(SCBEngine scbEngine) { this.scbEngine = scbEngine; } @Override public String getName() { return BootStrapProperties.readServiceName(scbEngine.getEnvironment()); } @Override public String getVersion() { return BootStrapProperties.readServiceVersion(scbEngine.getEnvironment()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.governance; import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public class ServiceCombRetryExtension extends AbstractRetryExtension { @Override protected String extractStatusCode(Object result) { if (!(result instanceof Response)) { return null; } Response resp = (Response) result; if (resp.isFailed()) { if (resp.getResult() instanceof InvocationException) { InvocationException e = resp.getResult(); return String.valueOf(e.getStatusCode()); } } return String.valueOf(resp.getStatusCode()); } @Override public boolean isFailedResult(List statusList, Throwable e) { if (e instanceof InvocationException) { InvocationException invocationException = (InvocationException) e; return statusList.contains(String.valueOf(invocationException.getStatusCode())); } return super.isFailedResult(statusList, e); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.List; import org.apache.servicecomb.core.invocation.timeout.PassingTimeStrategy; import org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; @Configuration public class CoreInvocationConfiguration { @Bean public InvocationTimeoutBootListener scbInvocationTimeoutBootListener(EventBus eventBus, List strategies, Environment environment) { return new InvocationTimeoutBootListener(eventBus, strategies, environment); } @Bean public PassingTimeStrategy scbPassingTimeStrategy() { return new PassingTimeStrategy(); } @Bean public ProcessingTimeStrategy scbProcessingTimeStrategy() { return new ProcessingTimeStrategy(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; public interface InvocationCreator { CompletableFuture createAsync(); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/InvocationFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; public final class InvocationFactory { private InvocationFactory() { } public static Invocation forConsumer(ReferenceConfig referenceConfig, OperationMeta operationMeta, InvocationRuntimeType invocationRuntimeType, Map swaggerArguments) { Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, swaggerArguments); return setSrcMicroservice(invocation); } public static Invocation setSrcMicroservice(Invocation invocation) { invocation.addContext(CoreConst.SRC_MICROSERVICE, BootStrapProperties.readServiceName(SCBEngine.getInstance().getEnvironment())); // TODO: hard code registry name here. This is an old feature not for all registry implementations. if (addSourceServiceId()) { invocation.addContext(CoreConst.SRC_SERVICE_ID, SCBEngine.getInstance().getRegistrationManager().getServiceId("sc-registration")); } if (addSourceInstanceId()) { invocation.addContext(CoreConst.SRC_INSTANCE_ID, SCBEngine.getInstance().getRegistrationManager().getInstanceId("sc-registration")); } return invocation; } public static boolean addSourceServiceId() { return SCBEngine.getInstance().getEnvironment(). getProperty("servicecomb.context.source.serviceId", boolean.class, true); } public static boolean addSourceInstanceId() { return SCBEngine.getInstance().getEnvironment(). getProperty("servicecomb.context.source.instanceId", boolean.class, true); } /* * transport server收到请求时,创建invocation */ public static Invocation forProvider(Endpoint endpoint, OperationMeta operationMeta, Map swaggerArguments) { SCBEngine.getInstance().ensureStatusUp(); return new Invocation(endpoint, operationMeta, swaggerArguments); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.core.Invocation; /** *
 * for consumer:
 *
 *  1. total: start create invocation -> all filters finished
 *  2. prepare: start create invocation -> finish create invocation
 *  3. filters-(filter-name): start call on filter -> on complete
 *  4. connection: start get connection -> finish get connection
 *  5. consumer-encode: start encode request -> finish encode request
 *  6. consumer-decode: start decode response -> finish decode response
 *  7. consumer-send: start send request -> finish send request
 *  8. wait: finish send request -> start decode response
 *
 * for provider:
 *  1. total: start create invocation -> all filters finished
 *  2. prepare: start create invocation -> finish create invocation
 *  3. filters-(filter-name): start call on filter -> on complete
 *  4. queue: add in queue -> execute in thread
 *  5. provider-decode: start decode request -> finish decode request
 *  6. provider-encode: start encode response -> finish encode response
 *  7. provider-send: start send response -> finish send response
 *  8. execute: start business execute -> finish business execute
 *
 * for edge:
 *
 *  *  1. total: start create invocation -> all filters finished
 *  *  2. prepare: start create invocation -> finish create invocation
 *  *  3. filters-(filter-name): start call on filter -> on complete
 *  *  4. connection: start get connection -> finish get connection
 *  *  5. provider-decode: start decode request -> finish decode request
 *  *  6. provider-encode: start encode response -> finish encode response
 *  *  7. consumer-encode: start encode request -> finish encode request
 *  *  8. consumer-decode: start decode response -> finish decode response
 *  *  9. consumer-send: start send request -> finish send request
 *  *  10. provider-send: start send response -> finish send response
 *  *  11. wait: finish send request -> start decode response
 *
 * 
*/ public class InvocationStageTrace { public static class Stage { private long beginTime; private long endTime; public long getBeginTime() { return beginTime; } public long getEndTime() { return endTime; } } public static final String STAGE_TOTAL = "total"; public static final String STAGE_PREPARE = "prepare"; public static final String STAGE_PROVIDER_QUEUE = "queue"; public static final String STAGE_PROVIDER_DECODE_REQUEST = "provider-decode"; public static final String STAGE_PROVIDER_ENCODE_RESPONSE = "provider-encode"; public static final String STAGE_PROVIDER_SEND = "provider-send"; public static final String STAGE_PROVIDER_BUSINESS = "execute"; public static final String STAGE_CONSUMER_CONNECTION = "connection"; public static final String STAGE_CONSUMER_ENCODE_REQUEST = "consumer-encode"; public static final String STAGE_CONSUMER_DECODE_RESPONSE = "consumer-decode"; public static final String STAGE_CONSUMER_SEND = "consumer-send"; public static final String STAGE_CONSUMER_WAIT = "wait"; private final Invocation invocation; // invocation start time in millis, for passing strategy use only private long startInMillis; // invocation start time in nanos, for passing strategy use only private long start; private long finish; private long startCreateInvocation; private long finishCreateInvocation; private long startProviderQueue; private long finishProviderQueue; private long startConsumerConnection; private long finishConsumerConnection; private long startProviderDecodeRequest; private long finishProviderDecodeRequest; private long startProviderEncodeResponse; private long finishProviderEncodeResponse; private long startConsumerEncodeRequest; private long finishConsumerEncodeRequest; private long startConsumerDecodeResponse; private long finishConsumerDecodeResponse; private long startProviderSendResponse; private long finishProviderSendResponse; private long startConsumerSendRequest; private long finishConsumerSendRequest; private long startBusinessExecute; private long finishBusinessExecute; private long startWaitResponse; private long finishWaitResponse; // invocation stage can not be used in concurrent access private final Map stages = new HashMap<>(); public InvocationStageTrace(Invocation invocation) { this.invocation = invocation; } public String recordStageBegin(String stageName) { String realStageName = stageName; while (stages.get(realStageName) != null) { realStageName = realStageName + "@"; } Stage stage = new Stage(); stage.beginTime = System.nanoTime(); stages.put(realStageName, stage); return realStageName; } public void recordStageEnd(String realStageName) { Stage stage = stages.get(realStageName); stage.endTime = nanoTime(); } public Map getStages() { return stages; } public void finish() { this.finish = nanoTime(); } public void startCreateInvocation(long nano) { this.startCreateInvocation = nano; this.startInMillis = millisTime(); this.start = nanoTime(); } public void finishCreateInvocation() { this.finishCreateInvocation = nanoTime(); } public long calcPrepare() { return calc(finishCreateInvocation, startCreateInvocation); } public void startProviderQueue() { this.startProviderQueue = nanoTime(); } public void finishProviderQueue() { this.finishProviderQueue = nanoTime(); } public long calcQueue() { return calc(finishProviderQueue, startProviderQueue); } public void startProviderDecodeRequest() { this.startProviderDecodeRequest = nanoTime(); } public void finishProviderDecodeRequest() { this.finishProviderDecodeRequest = nanoTime(); } public long calcProviderDecodeRequest() { return calc(finishProviderDecodeRequest, startProviderDecodeRequest); } public void startProviderEncodeResponse() { this.startProviderEncodeResponse = nanoTime(); } public void finishProviderEncodeResponse() { this.finishProviderEncodeResponse = nanoTime(); } public long calcProviderEncodeResponse() { return calc(finishProviderEncodeResponse, startProviderEncodeResponse); } public void startConsumerEncodeRequest() { this.startConsumerEncodeRequest = nanoTime(); } public void finishConsumerEncodeRequest() { this.finishConsumerEncodeRequest = nanoTime(); } public long calcConsumerEncodeRequest() { return calc(finishConsumerEncodeRequest, startConsumerEncodeRequest); } public void startConsumerDecodeResponse() { this.startConsumerDecodeResponse = nanoTime(); } public void finishConsumerDecodeResponse() { this.finishConsumerDecodeResponse = nanoTime(); } public long calcConsumerDecodeResponse() { return calc(finishConsumerDecodeResponse, startConsumerDecodeResponse); } public void startProviderSendResponse() { this.startProviderSendResponse = nanoTime(); } public void finishProviderSendResponse() { this.finishProviderSendResponse = nanoTime(); } public long calcProviderSendResponse() { return calc(finishProviderSendResponse, startProviderSendResponse); } public void startBusinessExecute() { this.startBusinessExecute = nanoTime(); } public void finishBusinessExecute() { this.finishBusinessExecute = nanoTime(); } public long calcBusinessExecute() { return calc(finishBusinessExecute, startBusinessExecute); } public void startConsumerConnection() { this.startConsumerConnection = nanoTime(); } public void finishConsumerConnection() { this.finishConsumerConnection = nanoTime(); } public long calcConnection() { return calc(finishConsumerConnection, startConsumerConnection); } public void startConsumerSendRequest() { this.startConsumerSendRequest = nanoTime(); } public void finishConsumerSendRequest() { this.finishConsumerSendRequest = nanoTime(); } public long calcConsumerSendRequest() { return calc(finishConsumerSendRequest, startConsumerSendRequest); } public void startWaitResponse() { this.startWaitResponse = nanoTime(); } public void finishWaitResponse() { this.finishWaitResponse = nanoTime(); } public long calcWait() { return calc(finishWaitResponse, startWaitResponse); } public long calcTotal() { return calc(finish, this.startCreateInvocation); } public long getStartInMillis() { return this.startInMillis; } public long getStart() { return this.start; } public static long calc(long finish, long start) { if (finish == 0 || start == 0) { return 0; } return finish - start; } /* * Holder for testing purpose */ protected long nanoTime() { return System.nanoTime(); } protected long millisTime() { return System.currentTimeMillis(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.List; import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; import org.apache.servicecomb.core.invocation.timeout.PassingTimeStrategy; import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @SuppressWarnings({"unused"}) public final class InvocationTimeoutBootListener { private static final Logger LOGGER = LoggerFactory.getLogger(InvocationTimeoutBootListener.class); public static final String PREFIX = "servicecomb.invocation.timeout.check"; public static final String STRATEGY = PREFIX + ".strategy"; public static final String ENABLED = PREFIX + ".enabled"; private final InvocationTimeoutStrategy strategy; public InvocationTimeoutBootListener(EventBus eventBus, List strategies, Environment environment) { if (!environment.getProperty(ENABLED, boolean.class, false)) { strategy = null; return; } String strategyName = environment.getProperty(STRATEGY, PassingTimeStrategy.NAME); // if strategyName is wrong, then just throw exception strategy = strategies.stream() .filter(invocationTimeoutStrategy -> strategyName.equals(invocationTimeoutStrategy.name())) .findFirst() .orElseThrow(() -> new IllegalStateException("can not find InvocationTimeoutStrategy, name=" + strategyName)); eventBus.register(this); } @Subscribe @EnableExceptionPropagation public void onInvocationTimeoutCheckEvent(InvocationTimeoutCheckEvent event) { strategy.checkTimeout(event.getInvocation()); } @Subscribe public void onInvocationStartEvent(InvocationStartEvent event) { strategy.start(event.getInvocation()); } @Subscribe @EnableExceptionPropagation public void onInvocationBusinessMethodStartEvent(InvocationBusinessMethodStartEvent event) { strategy.startBusinessMethod(event.getInvocation()); } @Subscribe @EnableExceptionPropagation public void onInvocationBusinessFinishEvent(InvocationBusinessFinishEvent event) { strategy.finishBusinessMethod(event.getInvocation()); } @Subscribe @EnableExceptionPropagation public void onInvocationStartSendRequestEvent(InvocationStartSendRequestEvent event) { strategy.beforeSendRequest(event.getInvocation()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionCodes; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public interface InvocationTimeoutStrategy { // indicate whether invocation already timeout inside a process // null is not timeout // other value is timeout String CHAIN_ALREADY_TIMED_OUT = "x-scb-chain-timed-out"; String name(); void start(Invocation invocation); default void startRunInExecutor(Invocation invocation) { checkTimeout(invocation); } default void startBusinessMethod(Invocation invocation) { checkTimeout(invocation); } default void finishBusinessMethod(Invocation invocation) { checkTimeout(invocation); } default void beforeSendRequest(Invocation invocation) { checkTimeout(invocation); } default void checkTimeout(Invocation invocation) { long nanoInvocationTimeout = invocation.getOperationMeta().getConfig().getNanoInvocationTimeout(); if (nanoInvocationTimeout <= 0 || alreadyTimeout(invocation)) { return; } long nanoTime = calculateElapsedNanoTime(invocation); if (nanoTime <= nanoInvocationTimeout) { return; } invocation.addLocalContext(CHAIN_ALREADY_TIMED_OUT, true); throw new InvocationException(REQUEST_TIMEOUT, ExceptionCodes.INVOCATION_TIMEOUT, "Invocation Timeout."); } default boolean alreadyTimeout(Invocation invocation) { return invocation.getLocalContext(CHAIN_ALREADY_TIMED_OUT) != null; } long calculateElapsedNanoTime(Invocation invocation); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jakarta.ws.rs.core.Response.Status; public abstract class ProducerInvocationFlow { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerInvocationFlow.class); private final long startTime = System.nanoTime(); private final InvocationCreator invocationCreator; protected final HttpServletRequestEx requestEx; protected final HttpServletResponseEx responseEx; public ProducerInvocationFlow(InvocationCreator invocationCreator) { this(invocationCreator, null, null); } public ProducerInvocationFlow(InvocationCreator invocationCreator, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { this.invocationCreator = invocationCreator; this.requestEx = requestEx; this.responseEx = responseEx; } public void run() { CompletableFuture.completedFuture(null) .thenCompose(v -> invocationCreator.createAsync()) .exceptionally(this::sendCreateInvocationException) .thenAccept(this::tryRunInvocation); } private void tryRunInvocation(Invocation invocation) { if (invocation == null) { return; } invocation.getInvocationStageTrace().startCreateInvocation(this.startTime); invocation.getInvocationStageTrace().finishCreateInvocation(); invocation.onStart(requestEx); if (invocation.isEdge()) { invocation.getMicroserviceMeta().getEdgeFilterChain() .onFilter(invocation) .whenComplete((response, throwable) -> { if (throwable != null) { // Server codec operates on Response. So the filter chain result should be Response and // will never throw exception. // Sometimes Server Codec will throw exception, e.g. Connection Closed. LOGGER.error("Maybe a fatal bug that should be addressed or codec exception for {}.", invocation.getInvocationQualifiedName(), throwable); response = Response.createFail(new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData("Internal error, check logs for details."))); } endResponse(invocation, response); finishInvocation(invocation, response); }); return; } invocation.getMicroserviceMeta().getProviderFilterChain() .onFilter(invocation) .whenComplete((response, throwable) -> { if (throwable != null) { // Server codec operates on Response. So the filter chain result should be Response and // will never throw exception. // Sometimes Server Codec will throw exception, e.g. Connection Closed. LOGGER.error("Maybe a fatal bug that should be addressed or codec exception for {}.", invocation.getInvocationQualifiedName(), throwable); response = Response.createFail(new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData("Internal error, check logs for details."))); } endResponse(invocation, response); finishInvocation(invocation, response); }); } private void finishInvocation(Invocation invocation, Response response) { invocation.onFinish(response); } protected abstract Invocation sendCreateInvocationException(Throwable throwable); protected abstract void endResponse(Invocation invocation, Response response); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointCacheUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import org.apache.servicecomb.core.Endpoint; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; public final class EndpointCacheUtils { private static final LoadingCache CACHE = CacheBuilder.newBuilder() .maximumSize(10000) .build(new CacheLoader() { @Override public Endpoint load(String uri) { return EndpointCacheUtils.create(uri); } }); /** * @param uri https://www.abc.com:12345 * @return endpoint */ public static Endpoint getOrCreate(String uri) { return CACHE.getUnchecked(uri); } public static Endpoint create(String uri) { return EndpointUtils.parse(EndpointUtils.formatFromUri(uri)); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointContextRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import java.lang.reflect.Type; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; public class EndpointContextRegister implements SwaggerContextRegister { @Override public Type getContextType() { return Endpoint.class; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import java.util.Map; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentMapper; public class EndpointMapper extends ConsumerArgumentMapper { private final String invocationArgumentName; public EndpointMapper(String invocationArgumentName) { this.invocationArgumentName = invocationArgumentName; } @Override public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments) { Invocation invocation = (Invocation) swaggerInvocation; invocation.setEndpoint((Endpoint) invocationArguments.get(invocationArgumentName)); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory; public class EndpointMapperFactory implements ConsumerContextArgumentMapperFactory { @Override public Class getContextClass() { return Endpoint.class; } @Override public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { return new EndpointMapper(invocationArgumentName); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import static com.google.common.collect.ImmutableMap.of; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; import org.apache.http.client.utils.URIBuilder; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.exception.Exceptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class EndpointUtils { private static final Logger LOGGER = LoggerFactory.getLogger(EndpointUtils.class); private static final String HTTP = "http"; private static final String HTTPS = "https"; private static final String H2C = "h2c"; private static final String H2 = "h2"; private static final String HTTP2 = "http2"; private static final int DEFAULT_HTTP_PORT = 80; private static final int DEFAULT_HTTPS_PORT = 443; private static class SchemeMeta { String protocol; boolean ssl; int defaultPort; public SchemeMeta(String protocol, boolean ssl, int defaultPort) { this.protocol = protocol; this.ssl = ssl; this.defaultPort = defaultPort; } } private static final Map SCHEME_META_MAP = of( HTTP, new SchemeMeta(null, false, DEFAULT_HTTP_PORT), HTTPS, new SchemeMeta(null, true, DEFAULT_HTTPS_PORT), H2C, new SchemeMeta(HTTP2, false, DEFAULT_HTTP_PORT), H2, new SchemeMeta(HTTP2, true, DEFAULT_HTTPS_PORT) ); private EndpointUtils() { } /** * * @param uriEndpoint eg: rest://xxx?sslEnabled=true * @return Endpoint object */ public static Endpoint parse(String uriEndpoint) { URI uri = URI.create(uriEndpoint); Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(uri.getScheme()); if (transport == null) { LOGGER.error("not deployed transport, uri={}.", uriEndpoint); throw Exceptions.genericConsumer("the endpoint's transport is not found."); } return new Endpoint(transport, uriEndpoint); } /** * {@code * http://xxx -> rest://xxx * https://xxx -> rest://xxx?sslEnabled=true * * h2c://xxx -> rest://xxx?protocol=http2 * h2://xxx -> rest://xxx?sslEnabled=true&protocol=http2 * * xxx -> rest://xxx:port?protocol=http2 * xxx?a=a1 -> rest://xxx:port?a=a1&protocol=http2 * other://xxx -> other://xxx * } * * This method provided for convenience of handling user input endpoints, and do not have a strict meaning. Make sure all unit test cases * work before change. **/ public static String formatFromUri(String inputUri) { try { return doFormatFromUri(inputUri); } catch (URISyntaxException e) { throw new IllegalStateException("failed to convert uri to endpoint.", e); } } private static String doFormatFromUri(String inputUri) throws URISyntaxException { URIBuilder builder = new URIBuilder(inputUri); if (builder.getScheme() == null) { builder.setScheme(H2C); builder.setHost(extractHostFromPath(builder)); builder.setPath(null); } SchemeMeta schemeMeta = SCHEME_META_MAP.get(builder.getScheme()); if (schemeMeta == null) { return inputUri; } return format(builder, schemeMeta); } private static String extractHostFromPath(URIBuilder builder) { String path = builder.getPath(); if (path == null) { return null; } if (path.startsWith("/")) { path = path.substring(1); } if (path.contains("/")) { path = path.substring(0, path.indexOf("/")); } return path; } private static String format(URIBuilder builder, SchemeMeta schemeMeta) throws URISyntaxException { if (schemeMeta.ssl) { builder.addParameter("sslEnabled", "true"); } if (schemeMeta.protocol != null) { builder.addParameter("protocol", schemeMeta.protocol); } if (builder.getPort() == -1) { builder.setPort(schemeMeta.defaultPort); } return builder.setScheme("rest").build().toString(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.timeout; import java.time.Clock; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationTimeoutStrategy; /** *
 * based on time synchronization
 * any time to calculate timeout: now - start time of invocation chain
 *
 * consumer: c
 * producer: p
 * ---------------------------------------------------------------
 * | process 2                                                   |
 * |                 c-send(T5)                       c-send(T8) |
 * |                    ↑                               ↑      |
 * | p-start(T3) → c-start(T4) → p-operation(T6) → c-start(T7)|
 * -----↑--------------------------------------------------------
 *      ↑
 * -----↑-----------------
 * |    ↑     process 1  |
 * |  c-send(T2)          |
 * |    ↑                |
 * |  c-start(T1)         |
 * ------------------------
 *
 * T2 timeout: T2 - T1
 * T3 timeout: T3 - T1
 * T4 timeout: T4 - T1
 * ......
 * 
*/ public class PassingTimeStrategy implements InvocationTimeoutStrategy { public static final String NAME = "passing-time"; // milliseconds // depend on time synchronization // transfer between processes public static final String CHAIN_START_TIME = "x-scb-chain-start"; private Clock clock = Clock.systemDefaultZone(); public PassingTimeStrategy setClock(Clock clock) { this.clock = clock; return this; } @Override public String name() { return NAME; } @Override public void start(Invocation invocation) { if (invocation.getLocalContext(CHAIN_START_TIME) != null) { return; } long startTimeMillis = invocation.getInvocationStageTrace().getStartInMillis(); String contextChainStartTime = invocation.getContext(CHAIN_START_TIME); if (StringUtils.isEmpty(contextChainStartTime)) { invocation.addContext(CHAIN_START_TIME, String.valueOf(startTimeMillis)); invocation.addLocalContext(CHAIN_START_TIME, startTimeMillis); return; } long chainStartTime = NumberUtils.toLong(contextChainStartTime, startTimeMillis); invocation.addLocalContext(CHAIN_START_TIME, chainStartTime); } @Override public long calculateElapsedNanoTime(Invocation invocation) { long passingTimeMillis = clock.millis() - invocation.getLocalContext(CHAIN_START_TIME); return TimeUnit.MILLISECONDS.toNanos(passingTimeMillis); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.timeout; import org.apache.commons.lang3.math.NumberUtils; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationTimeoutStrategy; import com.google.common.base.Ticker; /** *
 * Cumulative Processing Time
 * not depend on time synchronization
 * but lost network and framework outside of servicecomb processing time
 *
 * consumer: c
 * producer: p
 * ---------------------------------------------------------------
 * | process 2                                                   |
 * |                 c-send(T5)                       c-send(T8) |
 * |                    ↑                               ↑      |
 * | p-start(T3) → c-start(T4) → p-operation(T6) → c-start(T7)|
 * -----↑--------------------------------------------------------
 *      ↑
 * -----↑-----------------
 * |    ↑     process 1  |
 * |  c-send(T2)          |
 * |    ↑                |
 * |  c-start(T1)         |
 * ------------------------
 *
 * T2 timeout: T2 - T1
 * T3 timeout: (T2 - T1) + (T3 - T3)
 * T4 timeout: (T2 - T1) + (T4 - T3)
 * T5 timeout: (T2 - T1) + (T5 - T3)
 * T6 timeout: (T2 - T1) + (T6 - T3)
 * T7 timeout: (T2 - T1) + (T7 - T3)
 * T8 timeout: (T2 - T1) + (T8 - T3)
 * ......
 * 
*/ public class ProcessingTimeStrategy implements InvocationTimeoutStrategy { public static final String NAME = "processing-time"; // nanoseconds // used inside one process // not depend on time synchronization public static final String CHAIN_START_TIME = "x-scb-process-chain-start"; // nanoseconds // processing time of all previous process // transfer between processes public static final String CHAIN_PROCESSING = "x-scb-processing-time"; private Ticker ticker = Ticker.systemTicker(); public ProcessingTimeStrategy setTicker(Ticker ticker) { this.ticker = ticker; return this; } @Override public String name() { return NAME; } @Override public void start(Invocation invocation) { initProcessChainStart(invocation); initChainProcessing(invocation); } private void initProcessChainStart(Invocation invocation) { if (invocation.getLocalContext(CHAIN_START_TIME) != null) { return; } invocation.addLocalContext(CHAIN_START_TIME, invocation.getInvocationStageTrace().getStart()); } private void initChainProcessing(Invocation invocation) { if (invocation.getLocalContext(CHAIN_PROCESSING) != null) { return; } String contextChainProcessing = invocation.getContext(CHAIN_PROCESSING); long chainProcessingTime = NumberUtils.toLong(contextChainProcessing, 0L); invocation.addLocalContext(CHAIN_PROCESSING, chainProcessingTime); } @Override public void beforeSendRequest(Invocation invocation) { InvocationTimeoutStrategy.super.beforeSendRequest(invocation); long processingTime = calculateElapsedNanoTime(invocation); invocation.addContext(CHAIN_PROCESSING, Long.toString(processingTime)); } @Override public long calculateElapsedNanoTime(Invocation invocation) { long chainStartTime = invocation.getLocalContext(CHAIN_START_TIME); long previousProcessingTime = invocation.getLocalContext(CHAIN_PROCESSING); return ticker.read() - chainStartTime + previousProcessingTime; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider; import java.util.Collections; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.OpenAPI; /** * register and load OpenAPI from local file store or memory */ public class LocalOpenAPIRegistry implements OpenAPIRegistry { private static final Logger LOGGER = LoggerFactory.getLogger(LocalOpenAPIRegistry.class); // first key : appId // second key: microservice short name // third key : schemaId private final Map>> apps = new ConcurrentHashMapEx<>(); private final Environment environment; private OpenAPIChangeListener openAPIChangeListener; public LocalOpenAPIRegistry(Environment environment) { this.environment = environment; } @Override public boolean enabled() { return true; } @Override public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { apps.computeIfAbsent(application, k -> new ConcurrentHashMapEx<>()) .computeIfAbsent(serviceName, k -> new ConcurrentHashMapEx<>()) .put(schemaId, api); openAPIChangeListener.onOpenAPIChanged(application, serviceName); LOGGER.info("register swagger appId={}, name={}, schemaId={}.", application, serviceName, schemaId); } /** * Method for retrieve myself schema contents. */ public Map loadOpenAPI() { return loadOpenAPI(BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment)); } @Override public Map loadOpenAPI(String application, String serviceName) { if (apps.get(application) != null && apps.get(application).get(serviceName) != null) { return apps.get(application).get(serviceName); } return Collections.emptyMap(); } @Override public void setOpenAPIChangeListener(OpenAPIChangeListener listener) { this.openAPIChangeListener = listener; } @Override public int getOrder() { return -10000; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider; import java.util.Map; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.springframework.core.Ordered; import io.swagger.v3.oas.models.OpenAPI; /** * Register and load OpenAPI extensions. */ public interface OpenAPIRegistry extends Ordered { String CONFIG_PREFIX = "servicecomb.openAPI.registry"; boolean enabled(); void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api); Map loadOpenAPI(String application, String serviceName); void setOpenAPIChangeListener(OpenAPIChangeListener listener); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.commons.io.FilenameUtils; import org.apache.servicecomb.foundation.common.utils.ResourceUtil; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; import io.swagger.v3.oas.models.OpenAPI; /** * Register and load OpenAPI from various OpenAPIRegistry */ public class OpenAPIRegistryManager { private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIRegistryManager.class); public interface OpenAPIChangeListener { void onOpenAPIChanged(String application, String serviceName); } private List openAPIRegistries; private final List changeListeners = new ArrayList<>(); @Autowired public void setOpenAPIRegistries(List openAPIRegistries) { List target = new ArrayList<>(openAPIRegistries.size()); for (OpenAPIRegistry registry : openAPIRegistries) { if (registry.enabled()) { registry.setOpenAPIChangeListener(this::onOpenAPIChanged); target.add(registry); } } this.openAPIRegistries = target; } public void addOpenAPIChangeListener(OpenAPIChangeListener changeListener) { this.changeListeners.add(changeListener); } public void onOpenAPIChanged(String application, String serviceName) { for (OpenAPIChangeListener listener : this.changeListeners) { try { listener.onOpenAPIChanged(application, serviceName); } catch (Exception e) { LOGGER.warn("event process error {}/{}, {}", application, serviceName, e.getMessage()); } } } public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { for (OpenAPIRegistry registry : this.openAPIRegistries) { registry.registerOpenAPI(application, serviceName, schemaId, api); } } public void registerOpenAPI(String application, String serviceName, String schemaId, Class cls) { OpenAPI api = SwaggerGenerator.generate(cls); registerOpenAPI(application, serviceName, schemaId, api); } public void registerOpenAPIInLocation(String application, String serviceName, String swaggersLocation) { try { List resourceUris = ResourceUtil.findResourcesBySuffix(swaggersLocation, ".yaml"); if (resourceUris.isEmpty()) { return; } for (URI uri : resourceUris) { URL url = uri.toURL(); OpenAPI swagger = SwaggerUtils.parseAndValidateSwagger(url); String schemaId = FilenameUtils.getBaseName(url.getPath()); registerOpenAPI(application, serviceName, schemaId, swagger); } } catch (Throwable e) { throw new IllegalStateException(String.format( "failed to register swaggers, microserviceName=%s, location=%s.", serviceName, swaggersLocation), e); } } public Map loadOpenAPI(String appId, String microserviceName) { for (OpenAPIRegistry registry : this.openAPIRegistries) { Map result = registry.loadOpenAPI(appId, microserviceName); if (!CollectionUtils.isEmpty(result)) { return result; } } return Collections.emptyMap(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.router.util.VersionCompareUtil; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import io.swagger.v3.oas.models.OpenAPI; import jakarta.ws.rs.core.Response.Status; /** * register and load OpenAPI from registration and discovery registry. */ public class RegistryOpenAPIRegistry implements OpenAPIRegistry { private DiscoveryManager discoveryManager; private Environment environment; @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public boolean enabled() { return environment.getProperty(OpenAPIRegistry.CONFIG_PREFIX + ".registry.enabled", boolean.class, false); } @Override public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { // do noting } @Override public Map loadOpenAPI(String application, String serviceName) { List discoveryInstances = discoveryManager.findServiceInstances(application, serviceName); if (discoveryInstances.isEmpty()) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); } discoveryInstances.sort((a, b) -> VersionCompareUtil.compareVersion(b.getVersion(), a.getVersion())); Map result = new HashMap<>(); String version = null; for (DiscoveryInstance instance : discoveryInstances) { if (version != null && !version.equals(instance.getVersion())) { break; } version = instance.getVersion(); instance.getSchemas().forEach((k, v) -> result.computeIfAbsent(k, (key) -> SwaggerUtils.parseSwagger(v))); } return result; } @Override public void setOpenAPIChangeListener(OpenAPIChangeListener listener) { this.discoveryManager.addInstanceChangeListener( (registryName, application, serviceName, instances) -> { if (CollectionUtils.isEmpty(instances)) { return; } listener.onOpenAPIChanged(application, serviceName); }); } @Override public int getOrder() { return -8000; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.foundation.common.utils.ResourceUtil; import org.apache.servicecomb.swagger.SwaggerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; public class ConsumerProviderManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerProviderManager.class); private final OpenAPIRegistryManager openAPIRegistryManager; private final Environment environment; public ConsumerProviderManager(Environment environment, OpenAPIRegistryManager openAPIRegistryManager) { this.environment = environment; this.openAPIRegistryManager = openAPIRegistryManager; } public void init() throws Exception { registerSwaggerFromApplications(); registerSwaggerFromMicroservices(); } private void registerSwaggerFromApplications() { try { List resourceUris = ResourceUtil.findResourcesBySuffix("applications", ".yaml"); for (URI uri : resourceUris) { String path = uri.toURL().getPath(); String[] segments = path.split("/"); if (segments.length < 4 || !"applications".equals(segments[segments.length - 4])) { continue; } openAPIRegistryManager.registerOpenAPI(segments[segments.length - 3], segments[segments.length - 2], segments[segments.length - 1].substring(0, segments[segments.length - 1].indexOf(".yaml")), SwaggerUtils.parseAndValidateSwagger(uri.toURL())); } } catch (IOException | URISyntaxException e) { LOGGER.error("Load schema ids failed from applications. {}.", e.getMessage()); } } private void registerSwaggerFromMicroservices() { try { List resourceUris = ResourceUtil.findResourcesBySuffix("microservices", ".yaml"); for (URI uri : resourceUris) { String path = uri.toURL().getPath(); String[] segments = path.split("/"); if (segments.length < 3 || !"microservices".equals(segments[segments.length - 3])) { continue; } openAPIRegistryManager.registerOpenAPI(BootStrapProperties.readApplication(environment), segments[segments.length - 2], segments[segments.length - 1].substring(0, segments[segments.length - 1].indexOf(".yaml")), SwaggerUtils.parseAndValidateSwagger(uri.toURL())); } } catch (IOException | URISyntaxException e) { LOGGER.error("Load schema ids failed from microservices. {}.", e.getMessage()); } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import static org.apache.servicecomb.core.exception.Exceptions.toConsumerResponse; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import io.vertx.core.Context; import jakarta.ws.rs.core.Response.Status; public final class InvokerUtils { @SuppressWarnings({"unchecked"}) public static T syncInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType) { Invocation invocation = createInvocation(microserviceName, transport, schemaId, operationId, swaggerArguments, responseType); return (T) syncInvoke(invocation); } public static void reactiveInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType, AsyncResponse asyncResp) { Invocation invocation = createInvocation(microserviceName, transport, schemaId, operationId, swaggerArguments, responseType); reactiveInvoke(invocation, asyncResp); } public static T syncInvoke(String microserviceName, String schemaId, String operationId, Map swaggerArguments, Type responseType) { return syncInvoke(microserviceName, null, schemaId, operationId, swaggerArguments, responseType); } public static void reactiveInvoke(String microserviceName, String schemaId, String operationId, Map swaggerArguments, Type responseType, AsyncResponse asyncResp) { reactiveInvoke(microserviceName, null, schemaId, operationId, swaggerArguments, responseType, asyncResp); } public static Invocation createInvocation(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType) { long startCreateInvocation = System.nanoTime(); MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance() .getOrCreateReferenceConfig(microserviceName); if (microserviceReferenceConfig == null) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData(String.format("Failed to invoke service %s. Maybe service" + " not registered or no active instance.", microserviceName))); } MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getMicroserviceMeta(); SchemaMeta schemaMeta = microserviceMeta.ensureFindSchemaMeta(schemaId); OperationMeta operationMeta = schemaMeta.ensureFindOperation(operationId); ReferenceConfig referenceConfig = microserviceReferenceConfig.createReferenceConfig(transport, operationMeta); InvocationRuntimeType invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); invocationRuntimeType.setSuccessResponseType(responseType); Invocation result = InvocationFactory .forConsumer(referenceConfig, operationMeta, invocationRuntimeType, swaggerArguments); result.getInvocationStageTrace().startCreateInvocation(startCreateInvocation); result.getInvocationStageTrace().finishCreateInvocation(); return result; } /** * * use of this method , the response type can not be determined. * use {@link #syncInvoke(String, String, String, String, Map, Type)} instead. * */ @Deprecated public static Object syncInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments) { return syncInvoke(microserviceName, transport, schemaId, operationId, swaggerArguments, null); } /** * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp */ public static Object syncInvoke(Invocation invocation) throws InvocationException { Response response = innerSyncInvoke(invocation); if (response.isSucceed()) { return response.getResult(); } throw ExceptionFactory.convertConsumerException(response.getResult()); } public static boolean isInEventLoop() { return Context.isOnEventLoopThread(); } /** * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp */ public static Response innerSyncInvoke(Invocation invocation) { if (isInEventLoop() && SCBEngine.getInstance() .getEnvironment() .getProperty("servicecomb.invocation.enableEventLoopBlockingCallCheck", boolean.class, true)) { throw new IllegalStateException("Can not execute sync logic in event loop."); } return toSync(invoke(invocation), invocation.getWaitTime()); } /** * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp */ public static void reactiveInvoke(Invocation invocation, AsyncResponse asyncResp) { invoke(invocation).whenComplete((r, e) -> { if (e == null) { asyncResp.complete(r); } else { asyncResp.consumerFail(e); } }); } public static boolean isSyncMethod(Method method) { return !isAsyncMethod(method); } public static boolean isAsyncMethod(Method method) { // currently only support CompletableFuture for async method definition return method.getReturnType().equals(CompletableFuture.class); } public static T toSync(CompletableFuture future, long waitInMillis) { try { if (waitInMillis > 0) { return future.get(waitInMillis, TimeUnit.MILLISECONDS); } return future.get(); } catch (ExecutionException executionException) { throw AsyncUtils.rethrow(executionException.getCause()); } catch (TimeoutException timeoutException) { throw new InvocationException(Status.REQUEST_TIMEOUT, new CommonExceptionData("Invocation Timeout."), timeoutException); } catch (Throwable e) { throw AsyncUtils.rethrow(e); } } /** * This method is used in new Filter implementation to replace Handler * NOTE: this method should never throw exception directly */ public static CompletableFuture invoke(Invocation invocation) { invocation.onStart(null); return invocation.getMicroserviceMeta().getConsumerFilterChain() .onFilter(invocation) .exceptionally(throwable -> toConsumerResponse(invocation, throwable)) .whenComplete((response, throwable) -> invocation.onFinish(response)); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/MicroserviceReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; /** * microservice meta data for consumer. */ public class MicroserviceReferenceConfig { private final String appId; private final String microserviceName; private final MicroserviceMeta microserviceMeta; public MicroserviceReferenceConfig( String appId, String microserviceName, MicroserviceMeta microserviceMeta) { this.appId = appId; this.microserviceName = microserviceName; this.microserviceMeta = microserviceMeta; } public MicroserviceMeta getMicroserviceMeta() { if (microserviceMeta == null) { throw new IllegalStateException( String.format( "Probably invoke a service before it is registered, or no instance found for it, appId=%s, name=%s.", appId, microserviceName)); } return microserviceMeta; } public ReferenceConfig createReferenceConfig(OperationMeta operationMeta) { return createReferenceConfig(null, operationMeta); } public ReferenceConfig createReferenceConfig(String transport, OperationMeta operationMeta) { if (transport == null) { transport = operationMeta.getConfig().getTransport(); } final ReferenceConfig referenceConfig = new ReferenceConfig(transport); return referenceConfig; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReactiveResponseExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import java.util.concurrent.Executor; /** * reactive场景,就地执行即可 */ public class ReactiveResponseExecutor implements Executor { @Override public void execute(Runnable cmd) { cmd.run(); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; // operation level, to keep compatible, not change name public class ReferenceConfig { protected String transport; public ReferenceConfig(String transport) { this.transport = transport; } public String getTransport() { return transport; } public void setTransport(String transport) { this.transport = transport; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.SCBEngine.CreateMicroserviceMetaEvent; import org.apache.servicecomb.core.definition.ConsumerMicroserviceVersionsMeta; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.executor.VertxWorkerExecutor; import org.apache.servicecomb.registry.definition.MicroserviceNameParser; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import io.swagger.v3.oas.models.OpenAPI; import jakarta.ws.rs.core.Response.Status; public class ReferenceConfigManager { // application -> microservice name private final Map> referenceConfigs = new ConcurrentHashMapEx<>(); private final Map> referenceConfigsLocks = new ConcurrentHashMapEx<>(); private OpenAPIRegistryManager openAPIRegistryManager; @Autowired public void setOpenAPIRegistryManager(OpenAPIRegistryManager openAPIRegistryManager) { this.openAPIRegistryManager = openAPIRegistryManager; this.openAPIRegistryManager.addOpenAPIChangeListener(this::onOpenAPIChanged); } private void onOpenAPIChanged(String application, String serviceName) { if (referenceConfigs.get(application) != null && referenceConfigs.get(application).get(serviceName) != null) { MicroserviceReferenceConfig config = buildMicroserviceReferenceConfig(SCBEngine.getInstance(), application, serviceName); referenceConfigs.get(application).put(serviceName, config); } } public CompletableFuture getOrCreateReferenceConfigAsync (SCBEngine scbEngine, String qualifiedName) { MicroserviceNameParser parser = parseMicroserviceName(scbEngine, qualifiedName); MicroserviceReferenceConfig config = referenceConfigs.computeIfAbsent(parser.getAppId(), app -> new ConcurrentHashMapEx<>()) .get(parser.getMicroserviceName()); if (config != null) { return CompletableFuture.completedFuture(config); } if (InvokerUtils.isInEventLoop()) { CompletableFuture result = new CompletableFuture<>(); VertxWorkerExecutor executor = new VertxWorkerExecutor(); executor.execute(() -> { synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { try { MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) .get(parser.getMicroserviceName()); if (temp != null) { result.complete(temp); return; } temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), parser.getMicroserviceName()); referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); result.complete(temp); } catch (Exception e) { result.completeExceptionally(e); } } }); return result; } else { synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) .get(parser.getMicroserviceName()); if (temp != null) { return CompletableFuture.completedFuture(temp); } temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), parser.getMicroserviceName()); referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); return CompletableFuture.completedFuture(temp); } } } public MicroserviceReferenceConfig getOrCreateReferenceConfig(SCBEngine scbEngine, String qualifiedName) { MicroserviceNameParser parser = parseMicroserviceName(scbEngine, qualifiedName); MicroserviceReferenceConfig config = referenceConfigs.computeIfAbsent(parser.getAppId(), app -> new ConcurrentHashMapEx<>()) .get(parser.getMicroserviceName()); if (config == null) { synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { config = referenceConfigs.get(parser.getAppId()).get(parser.getMicroserviceName()); if (config != null) { return config; } config = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), parser.getMicroserviceName()); referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), config); return config; } } return config; } private MicroserviceNameParser parseMicroserviceName(SCBEngine scbEngine, String microserviceName) { return new MicroserviceNameParser(scbEngine.getAppId(), microserviceName); } private MicroserviceReferenceConfig buildMicroserviceReferenceConfig(SCBEngine engine, String application, String microserviceName) { ConsumerMicroserviceVersionsMeta microserviceVersionsMeta = new ConsumerMicroserviceVersionsMeta(engine); MicroserviceMeta microserviceMeta = new MicroserviceMeta(engine, application, microserviceName, true); microserviceMeta.setConsumerFilterChain(engine.getFilterChainsManager() .findConsumerChain(application, microserviceName)); microserviceMeta.setEdgeFilterChain(engine.getFilterChainsManager() .findEdgeChain(application, microserviceName)); microserviceMeta.setMicroserviceVersionsMeta(microserviceVersionsMeta); Map schemas = this.openAPIRegistryManager.loadOpenAPI(application, microserviceName); for (Entry entry : schemas.entrySet()) { OpenAPI swagger = entry.getValue(); if (swagger != null) { microserviceMeta.registerSchemaMeta(entry.getKey(), entry.getValue()); continue; } throw new InvocationException(Status.INTERNAL_SERVER_ERROR, String.format("Swagger %s/%s/%s can not be empty or load swagger failed.", application, microserviceName, entry.getKey())); } EventManager.getEventBus().post(new CreateMicroserviceMetaEvent(microserviceMeta)); return new MicroserviceReferenceConfig(application, microserviceName, microserviceMeta); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionCodes; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; /** * 业务线程在阻塞等待着,不必另起线程 * 将应答流程包装为Runnable,先唤醒业务线程,再在业务线程中执行runnable */ public class SyncResponseExecutor implements Executor { private final CountDownLatch latch; private Runnable cmd; private Response response; public SyncResponseExecutor() { latch = new CountDownLatch(1); } @Override public void execute(Runnable cmd) { this.cmd = cmd; // one network thread, many connections, then this notify will be performance bottlenecks // if save to a queue, and other thread(s) to invoke countDown, will get good performance // but if have multiple network thread, this "optimization" will reduce performance // now not change this. latch.countDown(); } public Response waitResponse(Invocation invocation) throws InvocationException { guardedWait(invocation); // cmd为null,是没走execute,直接返回的场景 if (cmd != null) { cmd.run(); } return response; } public void setResponse(Response response) { this.response = response; if (cmd == null) { // 1. 走到这里,没有cmd,说明没走到网络线程,直接就返回了。 // 2. 或者在网络线程中没使用execute的方式返回,这会导致返回流程在网络线程中执行,虽然不合适,但是也不应该导致业务线程无法唤醒 latch.countDown(); } } private void guardedWait(Invocation invocation) throws InvocationException { long wait = getWaitTime(invocation); try { if (wait <= 0) { latch.await(); return; } if (latch.await(wait, TimeUnit.MILLISECONDS)) { return; } } catch (InterruptedException e) { //ignore } throw new InvocationException(REQUEST_TIMEOUT, ExceptionCodes.INVOCATION_TIMEOUT, "Invocation Timeout."); } private long getWaitTime(Invocation invocation) { if (invocation.getOperationMeta().getConfig().getMsInvocationTimeout() > 0) { // if invocation timeout configured, use it. return invocation.getOperationMeta().getConfig().getMsInvocationTimeout(); } // In invocation handlers, may call other microservices, invocation // timeout may be much longer than request timeout. // For simplicity, default 30000 or two times of request timeout. return Math.max(invocation.getOperationMeta().getConfig().getMsRequestTimeout() * 2, 30000); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/producer/AbstractProducerProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; import org.apache.servicecomb.core.ProducerProvider; public abstract class AbstractProducerProvider implements ProducerProvider { } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.ExecutorService; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.utils.IOUtils; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.swagger.SwaggerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.OpenAPI; public class ProducerBootListener implements BootListener { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerBootListener.class); private static final String PATTERN = File.separator + "microservices" + File.separator + "%s" + File.separator + "%s.yaml"; private static final String TMP_DIR = System.getProperty("java.io.tmpdir"); private RegistrationManager registrationManager; private Environment environment; @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void onAfterTransport(BootEvent event) { boolean exportToFile = environment.getProperty(CoreConst.SWAGGER_EXPORT_ENABLED, boolean.class, true); String filePath = environment.getProperty(CoreConst.SWAGGER_DIRECTORY, String.class, TMP_DIR) + PATTERN; if (exportToFile) { LOGGER.info("export microservice swagger file to path {}", filePath); } // register schema to microservice; MicroserviceMeta microserviceMeta = event.getScbEngine().getProducerMicroserviceMeta(); for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas().values()) { OpenAPI swagger = schemaMeta.getSwagger(); String content = SwaggerUtils.swaggerToString(swagger); if (exportToFile) { exportToFile(String.format(filePath, BootStrapProperties.readServiceName(environment), schemaMeta.getSchemaId()), content); } else { LOGGER.info("generate swagger for {}/{}/{}, swagger: {}", microserviceMeta.getAppId(), microserviceMeta.getMicroserviceName(), schemaMeta.getSchemaId(), content); } this.registrationManager.addSchema(schemaMeta.getSchemaId(), content); } } // bug: can not close all thread for edge @Override public void onAfterClose(BootEvent event) { MicroserviceMeta microserviceMeta = event.getScbEngine().getProducerMicroserviceMeta(); if (microserviceMeta == null) { return; } for (OperationMeta operationMeta : microserviceMeta.getOperations()) { if (operationMeta.getExecutor() instanceof ExecutorService) { ((ExecutorService) operationMeta.getExecutor()).shutdown(); continue; } if (operationMeta.getExecutor() instanceof Closeable) { IOUtils.closeQuietly((Closeable) operationMeta.getExecutor()); continue; } LOGGER.warn("Executor {} do not support close or shutdown, it may block service shutdown.", operationMeta.getExecutor().getClass().getName()); } } private void exportToFile(String fileName, String content) { File file = new File(fileName); if (!file.getParentFile().exists()) { if (!file.getParentFile().mkdirs()) { LOGGER.error("create file directory failed"); return; } } if (file.exists()) { file.delete(); } try { file.createNewFile(); FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8, false); file.setReadOnly(); } catch (IOException e) { LOGGER.error("export swagger content to file failed, message: {}", e.getMessage()); } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; public class ProducerMeta { private String schemaId; private Object instance; private Class schemaInterface; public ProducerMeta() { } public ProducerMeta(String schemaId, Object instance) { this.schemaId = schemaId; this.instance = instance; } public String getSchemaId() { return schemaId; } public void setSchemaId(String schemaId) { this.schemaId = schemaId; } public Object getInstance() { return instance; } public void setInstance(Object instance) { this.instance = instance; } public Class getSchemaInterface() { return schemaInterface; } public void setSchemaInterface(Class schemaInterface) { this.schemaInterface = schemaInterface; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.ProducerProvider; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.CoreMetaUtils; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.oas.models.OpenAPI; public class ProducerProviderManager { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerProviderManager.class); private final List producerProviderList = new ArrayList<>( SPIServiceUtils.getOrLoadSortedService(ProducerProvider.class)); private final SCBEngine scbEngine; private final List producerMetas = new ArrayList<>(); public ProducerProviderManager(SCBEngine scbEngine) { this.scbEngine = scbEngine; } public List getProducerProviderList() { return producerProviderList; } public void init() { registerProducerMetas(producerMetas); for (ProducerProvider provider : producerProviderList) { List producerMetas = provider.init(); if (producerMetas == null) { LOGGER.warn("ProducerProvider {} not provide any producer.", provider.getClass().getName()); continue; } registerProducerMetas(producerMetas); } } public void addProducerMeta(String schemaId, Object instance) { addProducerMeta(new ProducerMeta(schemaId, instance)); } public void addProducerMeta(ProducerMeta producerMeta) { producerMetas.add(producerMeta); } private void registerProducerMetas(List producerMetas) { for (ProducerMeta producerMeta : producerMetas) { registerSchema(producerMeta.getSchemaId(), producerMeta.getSchemaInterface(), producerMeta.getInstance()); } } public SchemaMeta registerSchema(String schemaId, Object instance) { return registerSchema(schemaId, null, instance); } public SchemaMeta registerSchema(String schemaId, Class schemaInterface, Object instance) { MicroserviceMeta producerMicroserviceMeta = scbEngine.getProducerMicroserviceMeta(); SwaggerProducer swaggerProducer = scbEngine.getSwaggerEnvironment() .createProducer(instance, schemaInterface); OpenAPI swagger = swaggerProducer.getSwagger(); registerUrlPrefixToSwagger(swagger); // register self OpenAPI to registry scbEngine.getOpenAPIRegistryManager() .registerOpenAPI(BootStrapProperties.readApplication(scbEngine.getEnvironment()), BootStrapProperties.readServiceName(scbEngine.getEnvironment()), schemaId, swagger); SchemaMeta schemaMeta = producerMicroserviceMeta.registerSchemaMeta(schemaId, swagger); schemaMeta.putExtData(CoreMetaUtils.SWAGGER_PRODUCER, swaggerProducer); Executor reactiveExecutor = scbEngine.getExecutorManager().findExecutorById(ExecutorManager.EXECUTOR_REACTIVE); for (SwaggerProducerOperation producerOperation : swaggerProducer.getAllOperations()) { OperationMeta operationMeta = schemaMeta.ensureFindOperation(producerOperation.getOperationId()); operationMeta.setSwaggerProducerOperation(producerOperation); if (CompletableFuture.class.equals(producerOperation.getProducerMethod().getReturnType())) { operationMeta.setExecutor(scbEngine.getExecutorManager().findExecutor(operationMeta, reactiveExecutor)); } } return schemaMeta; } // This is special requirement by users: When service deployed in tomcat,user want to use RestTemplate to // call REST service by the full url. e.g. restTemplate.getForObject("cse://serviceName/root/prefix/health") // By default, user's do not need context prefix, e.g. restTemplate.getForObject("cse://serviceName/health") private void registerUrlPrefixToSwagger(OpenAPI swagger) { String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); if (!StringUtils.isEmpty(urlPrefix) && !SwaggerUtils.getBasePath(swagger).startsWith(urlPrefix) && scbEngine.getEnvironment().getProperty(DefinitionConst.REGISTER_URL_PREFIX, boolean.class, false)) { LOGGER.info("Add swagger base path prefix for {} with {}", SwaggerUtils.getBasePath(swagger), urlPrefix); SwaggerUtils.setBasePath(swagger, urlPrefix + SwaggerUtils.getBasePath(swagger)); } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.registry.discovery; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.registry.discovery.AbstractEndpointDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public class EndpointDiscoveryFilter extends AbstractEndpointDiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EndpointDiscoveryFilter.class); protected SCBEngine scbEngine; public EndpointDiscoveryFilter() { } @Autowired public void setScbEngine(SCBEngine scbEngine) { this.scbEngine = scbEngine; } @Override public int getOrder() { return Short.MAX_VALUE; } @Override protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent) { Invocation invocation = context.getInputParameters(); return invocation.getConfigTransportName(); } @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, StatefulDiscoveryInstance instance) { Transport transport = scbEngine.getTransportManager().findTransport(transportName); if (transport == null) { LOGGER.info("not deployed transport {}, ignore {}.", transportName, endpoint); return null; } return new Endpoint(transport, endpoint, instance); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/tracing/BraveTraceIdGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.tracing; import org.apache.servicecomb.core.CoreConst; import brave.internal.Platform; public class BraveTraceIdGenerator implements TraceIdGenerator { @Override public String getTraceIdKeyName() { return CoreConst.TRACE_ID_NAME; } @Override public String generate() { return Long.toHexString(Platform.get().nextTraceIdHigh()); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/tracing/ScbMarker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.tracing; import org.apache.servicecomb.foundation.common.log.AbstractMarker; public class ScbMarker extends AbstractMarker { private static final long serialVersionUID = -1L; private static final String MARKER_NAME = "SERVICECOMB_MARKER"; public ScbMarker() { } @Override public final String getName() { return MARKER_NAME; } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.tracing; public interface TraceIdGenerator { default int getOrder() { return 1000; } /** *
   *   for generators have the same name, will only use the minimum order instance
   *   not use getTraceIdKeyName to control this logic, because most customers not want to generate multiple traceIds
   * 
* @return generator name */ default String getName() { return "default"; } /** * * @return trance id key name *
   * default value is X-B3-TraceId to work with zipkin
   * 
*/ String getTraceIdKeyName(); String generate(); } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.tracing; import org.apache.servicecomb.core.Invocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.slf4j.Marker; public class TraceIdLogger { private static final Logger LOGGER = LoggerFactory.getLogger("scb-trace-id"); private static final Marker MARKER = new ScbMarker(); public static final String KEY_TRACE_ID = "SERVICECOMB_TRACE_ID"; private final Invocation invocation; public TraceIdLogger(Invocation invocation) { this.invocation = invocation; } public Invocation getInvocation() { return invocation; } public static String constructSource(String source) { return "[" + source + "(" + Thread.currentThread().getStackTrace()[2].getLineNumber() + ")]"; } public final String getName() { return invocation.getTraceId(); } public void error(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); LOGGER.error(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } public void warn(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); LOGGER.warn(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } public void info(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); LOGGER.info(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } public void debug(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); LOGGER.debug(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/transport/AbstractTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.transport; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.stream.Collectors; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.net.IpPort; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import io.vertx.core.Vertx; public abstract class AbstractTransport implements Transport { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransport.class); public static final String PUBLISH_ADDRESS = "servicecomb.service.publishAddress"; private static final String PUBLISH_PORT = "servicecomb.{transport_name}.publishPort"; /* * 用于参数传递:比如向RestServerVerticle传递endpoint地址。 */ public static final String ENDPOINT_KEY = "servicecomb.endpoint"; protected Vertx transportVertx; protected Endpoint endpoint; protected Endpoint publishEndpoint; protected Environment environment; @Override public Endpoint getPublishEndpoint() { return publishEndpoint; } @Override public Endpoint getEndpoint() { return endpoint; } @Override public void setEnvironment(Environment environment) { this.environment = environment; this.transportVertx = SharedVertxFactory.getSharedVertx(environment); } protected void setListenAddressWithoutSchema(String addressWithoutSchema) { setListenAddressWithoutSchema(addressWithoutSchema, null); } /* * 将配置的URI转换为endpoint * addressWithoutSchema 配置的URI,没有schema部分 */ protected void setListenAddressWithoutSchema(String addressWithoutSchema, Map pairs) { addressWithoutSchema = genAddressWithoutSchema(addressWithoutSchema, pairs); this.endpoint = new Endpoint(this, NetUtils.getRealListenAddress(getName(), addressWithoutSchema)); if (this.endpoint.getEndpoint() != null) { this.publishEndpoint = new Endpoint(this, getPublishAddress(getName(), addressWithoutSchema)); } else { this.publishEndpoint = null; } } private String genAddressWithoutSchema(String addressWithoutSchema, Map pairs) { if (addressWithoutSchema == null || pairs == null || pairs.isEmpty()) { return addressWithoutSchema; } int idx = addressWithoutSchema.indexOf('?'); if (idx == -1) { addressWithoutSchema += "?"; } else { addressWithoutSchema += "&"; } String encodedQuery = URLEncodedUtils.format( pairs.entrySet().stream().map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) .collect(Collectors.toList()), StandardCharsets.UTF_8.name()); addressWithoutSchema += encodedQuery; return addressWithoutSchema; } @Override public Object parseAddress(String address) { if (address == null) { return null; } return new URIEndpointObject(address); } /** * In the case that listening address configured as 0.0.0.0, the publish address will be determined * by the query result for the net interfaces. * * @return the publish address, or {@code null} if the param {@code address} is null. */ protected String getPublishAddress(String schema, String address) { if (address == null) { return address; } try { URI originalURI = new URI(schema + "://" + address); IpPort ipPort = NetUtils.parseIpPort(originalURI); if (ipPort == null) { LOGGER.warn("address {} not valid.", address); return null; } IpPort publishIpPort = genPublishIpPort(schema, ipPort); URIBuilder builder = new URIBuilder(originalURI); return builder.setHost(publishIpPort.getHostOrIp()).setPort(publishIpPort.getPort()).build().toString(); } catch (URISyntaxException e) { LOGGER.warn("address {} not valid.", address); return null; } } private IpPort genPublishIpPort(String schema, IpPort ipPort) { String publicAddressSetting = environment.getProperty(PUBLISH_ADDRESS, ""); publicAddressSetting = publicAddressSetting.trim(); String publishPortKey = PUBLISH_PORT.replace("{transport_name}", schema); int publishPortSetting = environment.getProperty(publishPortKey, int.class, 0); int publishPort = publishPortSetting == 0 ? ipPort.getPort() : publishPortSetting; if (publicAddressSetting.isEmpty()) { InetSocketAddress socketAddress = ipPort.getSocketAddress(); if (socketAddress.getAddress().isAnyLocalAddress()) { String host = NetUtils.getHostAddress(); if (Inet6Address.class.isInstance(socketAddress.getAddress())) { host = NetUtils.getIpv6HostAddress(); } LOGGER.warn("address {}, auto select a host address to publish {}:{}, maybe not the correct one", socketAddress, host, publishPort); return new IpPort(host, publishPort); } return ipPort; } if (publicAddressSetting.startsWith("{") && publicAddressSetting.endsWith("}")) { publicAddressSetting = NetUtils .ensureGetInterfaceAddress( publicAddressSetting.substring(1, publicAddressSetting.length() - 1)) .getHostAddress(); } return new IpPort(publicAddressSetting, publishPort); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.transport; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.oas.models.OpenAPI; public class TransportClassAnnotationProcessor implements ClassAnnotationProcessor { @Override public Type getProcessType() { return Transport.class; } @Override public void process(SwaggerGenerator swaggerGenerator, Transport transport) { OpenAPI swagger = swaggerGenerator.getOpenAPI(); if (StringUtils.isNotEmpty(transport.name())) { swagger.addExtension(CoreConst.TRANSPORT_NAME, transport.name()); } } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/transport/TransportManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.transport; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class TransportManager { private static final Logger LOGGER = LoggerFactory.getLogger(TransportManager.class); private final List transports = new ArrayList<>(SPIServiceUtils.getOrLoadSortedService(Transport.class)); private final Map transportMap = new HashMap<>(); public Map getTransportMap() { return transportMap; } private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } public void clearTransportBeforeInit() { transports.clear(); } public void addTransportBeforeInit(Transport transport) { this.transports.add(transport); } public void addTransportsBeforeInit(List transports) { this.transports.addAll(transports); } public void init(SCBEngine scbEngine) throws Exception { initEnvironment(); buildTransportMap(); for (Transport transport : transportMap.values()) { if (transport.init()) { Endpoint endpoint = transport.getPublishEndpoint(); if (endpoint != null && endpoint.getEndpoint() != null) { LOGGER.info("endpoint to publish: {}", endpoint.getEndpoint()); scbEngine.getRegistrationManager().addEndpoint(endpoint.getEndpoint()); } continue; } } } private void initEnvironment() { for (Transport transport : transports) { transport.setEnvironment(environment); } } protected void buildTransportMap() { Map> groups = groupByName(); for (Entry> entry : groups.entrySet()) { List group = entry.getValue(); checkTransportGroup(group); Transport transport = chooseOneTransport(group); transportMap.put(transport.getName(), transport); } } protected Transport chooseOneTransport(List group) { group.sort(Comparator.comparingInt(Transport::getOrder)); for (Transport transport : group) { if (transport.canInit()) { LOGGER.info("choose {} for {}.", transport.getClass().getName(), transport.getName()); return transport; } } throw new ServiceCombException( String.format("all transport named %s refused to init.", group.get(0).getName())); } protected void checkTransportGroup(List group) { // order value must be different, otherwise, maybe will choose a random transport Map orderMap = new HashMap<>(); for (Transport transport : group) { Transport existTransport = orderMap.putIfAbsent(transport.getOrder(), transport); if (existTransport != null) { throw new ServiceCombException(String.format("%s and %s have the same order %d", existTransport.getClass().getName(), transport.getClass().getName(), transport.getOrder())); } } } protected Map> groupByName() { Map> groups = new HashMap<>(); for (Transport transport : transports) { List list = groups.computeIfAbsent(transport.getName(), name -> new ArrayList<>()); list.add(transport); } return groups; } public Transport findTransport(String transportName) { return transportMap.get(transportName); } } ================================================ FILE: core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.transport; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.oas.models.Operation; public class TransportMethodAnnotationProcessor implements MethodAnnotationProcessor { @Override public Type getProcessType() { return Transport.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Transport transport) { Operation operation = operationGenerator.getOperation(); if (StringUtils.isNotEmpty(transport.name())) { operation.addExtension(CoreConst.TRANSPORT_NAME, transport.name()); } } } ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.exception.converter.ConstraintViolationExceptionConverter org.apache.servicecomb.core.exception.converter.InvocationExceptionConverter org.apache.servicecomb.core.exception.converter.IllegalArgumentExceptionConverter org.apache.servicecomb.core.exception.converter.TimeoutExceptionConverter org.apache.servicecomb.core.exception.converter.ConnectTimeoutExceptionConverter org.apache.servicecomb.core.exception.converter.DefaultExceptionConverter org.apache.servicecomb.core.exception.converter.ServiceCombExceptionConverter ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.filter.impl.DefaultFilterProvider ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.tracing.BraveTraceIdGenerator ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.transport.TransportClassAnnotationProcessor ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.transport.TransportMethodAnnotationProcessor ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.invocation.endpoint.EndpointContextRegister ================================================ FILE: core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.core.invocation.endpoint.EndpointMapperFactory ================================================ FILE: core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.core.filter.CoreFilterConfiguration org.apache.servicecomb.core.invocation.CoreInvocationConfiguration org.apache.servicecomb.core.governance.CoreGovernanceConfiguration org.apache.servicecomb.core.exception.CoreExceptionConfiguration org.apache.servicecomb.core.ServiceCombCoreConfiguration ================================================ FILE: core/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: -500 ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; public class Config { private static int clientThread; private static String transport; private static String mode; public static String getTransport() { return transport; } public static void setTransport(String transport) { Config.transport = transport; } public static int getClientThread() { return clientThread; } public static void setClientThread(int clientThread) { Config.clientThread = clientThread; } public static String getMode() { return mode; } public static void setMode(String mode) { Config.mode = mode; } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.util.HashMap; import java.util.Map; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestConfig { @Test public void testConstants() { Assertions.assertEquals("x-cse-context", CoreConst.CSE_CONTEXT); Assertions.assertEquals("rest", CoreConst.RESTFUL); Assertions.assertEquals("", CoreConst.ANY_TRANSPORT); Assertions.assertEquals("latest", CoreConst.VERSION_RULE_LATEST); Assertions.assertEquals("0.0.0.0+", CoreConst.DEFAULT_VERSION_RULE); } @Test public void testHttpResponse() { String objectString = new String("Unit Testing"); Response oResponse = Response.success(objectString, Status.OK); Assertions.assertTrue(oResponse.isSucceed()); oResponse = Response.succResp(objectString); Assertions.assertTrue(oResponse.isSucceed()); Assertions.assertEquals(200, oResponse.getStatusCode()); Throwable oThrowable = new Throwable("Error"); oResponse = Response.consumerFailResp(oThrowable); Assertions.assertTrue(oResponse.isFailed()); oResponse = Response.providerFailResp(oThrowable); Assertions.assertTrue(oResponse.isFailed()); } @Test public void testHttpStatus() { StatusType oStatus = new HttpStatus(204, "InternalServerError"); Assertions.assertEquals("InternalServerError", oStatus.getReasonPhrase()); } @Test public void testContextUtils() { ThreadLocal contextMgr = new ThreadLocal<>(); Assertions.assertEquals(contextMgr.get(), ContextUtils.getInvocationContext()); SwaggerInvocation invocation = new SwaggerInvocation(); invocation.addContext("test1", "testObject"); Assertions.assertEquals("testObject", invocation.getContext("test1")); Map context = new HashMap<>(); context.put("test2", new String("testObject")); invocation.setContext(context); Assertions.assertEquals(context, invocation.getContext()); invocation.setStatus(Status.OK); Assertions.assertEquals(200, invocation.getStatus().getStatusCode()); invocation.setStatus(204); Assertions.assertEquals(204, invocation.getStatus().getStatusCode()); invocation.setStatus(Status.OK); Assertions.assertEquals((Status.OK).getStatusCode(), invocation.getStatus().getStatusCode()); invocation.setStatus(203, "Done"); Assertions.assertEquals(203, invocation.getStatus().getStatusCode()); ContextUtils.setInvocationContext(invocation); Assertions.assertEquals(invocation, ContextUtils.getInvocationContext()); ContextUtils.removeInvocationContext(); Assertions.assertNull(ContextUtils.getInvocationContext()); } @Test public void testResponse() { Response response = Response.create(400, "test", null); InvocationException exception = response.getResult(); Assertions.assertNull(exception.getErrorData()); response = Response.create(400, "test", "errorData"); exception = response.getResult(); Assertions.assertEquals("errorData", exception.getErrorData()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.jndi.JndiPropertySource; public class TestConfigurationSpringInitializer { @BeforeEach public void beforeTest() { Configurator.setRootLevel(Level.OFF); Configurator.setRootLevel(Level.INFO); } @AfterEach public void afterTest() { } @Test public void testSetEnvironment() { ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources propertySources = new MutablePropertySources(); Map propertyMap = new HashMap<>(); final String map0Key0 = "map0-Key0"; final String map1Key0 = "map1-Key0"; final String map2Key0 = "map2-Key0"; final String map3Key0 = "map3-Key0"; propertyMap.put(map0Key0, "map0-Value0"); propertyMap.put(map1Key0, "map1-Value0"); propertyMap.put(map2Key0, "map2-Value0"); propertyMap.put(map3Key0, "map3-Value0"); /* propertySources |- compositePropertySource0 | |- mapPropertySource0 | | |- map0-Key0 = map0-Value0 | |- compositePropertySource1 | |- mapPropertySource1 | | |- map1-Key0 = map1-Value0 | |- mapPropertySource2 | |- map2-Key0 = map2-Value0 | |- JndiPropertySource(mocked) |- mapPropertySource3 |- map3-Key0 = map3-Value0 */ CompositePropertySource compositePropertySource0 = new CompositePropertySource("compositePropertySource0"); propertySources.addFirst(compositePropertySource0); HashMap map0 = new HashMap<>(); map0.put(map0Key0, propertyMap.get(map0Key0)); MapPropertySource mapPropertySource0 = new MapPropertySource("mapPropertySource0", map0); compositePropertySource0.addFirstPropertySource(mapPropertySource0); CompositePropertySource compositePropertySource1 = new CompositePropertySource("compositePropertySource1"); compositePropertySource0.addPropertySource(compositePropertySource1); HashMap map1 = new HashMap<>(); map1.put(map1Key0, propertyMap.get(map1Key0)); MapPropertySource mapPropertySource1 = new MapPropertySource("mapPropertySource1", map1); compositePropertySource1.addPropertySource(mapPropertySource1); HashMap map2 = new HashMap<>(); map2.put(map2Key0, propertyMap.get(map2Key0)); MapPropertySource mapPropertySource2 = new MapPropertySource("mapPropertySource2", map2); compositePropertySource1.addPropertySource(mapPropertySource2); compositePropertySource1.addPropertySource(Mockito.mock(JndiPropertySource.class)); HashMap map3 = new HashMap<>(); map3.put(map3Key0, propertyMap.get(map3Key0)); MapPropertySource mapPropertySource3 = new MapPropertySource("mapPropertySource3", map3); compositePropertySource0.addPropertySource(mapPropertySource3); Mockito.when(environment.getPropertySources()).thenReturn(propertySources); Mockito.doAnswer((Answer) invocation -> { Object[] args = invocation.getArguments(); String propertyName = (String) args[0]; if ("spring.config.name".equals(propertyName) || "spring.application.name".equals(propertyName)) { return null; } String value = propertyMap.get(propertyName); if (null == value) { Assertions.fail("get unexpected property name: " + propertyName); } return value; }).when(environment).getProperty(ArgumentMatchers.anyString(), ArgumentMatchers.eq(Object.class)); } @Test public void testSetEnvironmentOnEnvironmentName() { // get environment name from spring.config.name ConfigurableEnvironment environment0 = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources propertySources0 = new MutablePropertySources(); Mockito.when(environment0.getPropertySources()).thenReturn(propertySources0); Map map0 = new HashMap<>(1); map0.put("spring.config.name", "application"); propertySources0.addFirst(new MapPropertySource("mapPropertySource0", map0)); Mockito.when(environment0.getProperty("spring.config.name", Object.class)).thenReturn("application"); Mockito.when(environment0.getProperty("spring.config.name")).thenReturn("application"); // get environment name from spring.application.name ConfigurableEnvironment environment1 = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources propertySources1 = new MutablePropertySources(); Mockito.when(environment1.getPropertySources()).thenReturn(propertySources1); Map map1 = new HashMap<>(1); map1.put("spring.application.name", "bootstrap"); propertySources1.addFirst(new MapPropertySource("mapPropertySource1", map1)); Mockito.when(environment1.getProperty("spring.application.name", Object.class)).thenReturn("bootstrap"); Mockito.when(environment1.getProperty("spring.application.name")).thenReturn("bootstrap"); // get environment name from className+hashcode ConfigurableEnvironment environment2 = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources propertySources2 = new MutablePropertySources(); Mockito.when(environment2.getPropertySources()).thenReturn(propertySources2); Map map2 = new HashMap<>(1); map2.put("key2", "value2"); propertySources2.addFirst(new MapPropertySource("mapPropertySource2", map2)); Mockito.when(environment2.getProperty("key2", Object.class)).thenReturn("value2"); Mockito.when(environment2.getProperty("key2")).thenReturn("value2"); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestEndpoint { @Test public void testEndpoint() { Transport transport = Mockito.mock(Transport.class); Mockito.when(transport.parseAddress("rest://123.6.6.6:8080")).thenReturn("rest://123.6.6.6:8080"); Endpoint endpoint = new Endpoint(transport, "rest://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getAddress(), "rest://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getEndpoint(), "rest://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getTransport(), transport); Assertions.assertEquals(endpoint.toString(), "rest://123.6.6.6:8080"); } @Test public void testEndpointAddressConstructor() { Transport transport = Mockito.mock(Transport.class); StatefulDiscoveryInstance instance = Mockito.mock(StatefulDiscoveryInstance.class); Endpoint endpoint = new Endpoint(transport, "rest://123.6.6.6:8080", instance, "iot://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getAddress(), "iot://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getEndpoint(), "rest://123.6.6.6:8080"); Assertions.assertEquals(endpoint.getTransport(), transport); Assertions.assertEquals(endpoint.toString(), "rest://123.6.6.6:8080"); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.apache.servicecomb.core.exception.CseException; import org.apache.servicecomb.core.exception.ExceptionUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestException { @Test public void testCseException() { CseException oException = new CseException("500", "InternalServerError"); Assertions.assertEquals("500", oException.getCode()); Assertions.assertEquals("ServiceDefinitionException Code:500, Message:InternalServerError", oException.toString()); oException = new CseException("503", "OwnException", new Throwable()); Assertions.assertEquals("503", oException.getCode()); } @Test public void testExceptionUtils() { CseException oException = ExceptionUtils .createCseException("servicecomb.handler.ref.not.exist", new String("test")); Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); oException = ExceptionUtils.createCseException("servicecomb.handler.ref.not.exist", new Throwable(), new String("test")); Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); oException = ExceptionUtils.producerOperationNotExist("servicecomb.error", "unit-testing"); Assertions.assertEquals("servicecomb.producer.operation.not.exist", oException.getCode()); oException = ExceptionUtils.handlerRefNotExist("servicecomb.double.error"); Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); oException = ExceptionUtils.lbAddressNotFound("microServiceName", "my rule my world", "transportChannel"); Assertions.assertEquals("servicecomb.lb.no.available.address", oException.getCode()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.util.Arrays; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationBaseEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.core.tracing.BraveTraceIdGenerator; import org.apache.servicecomb.core.tracing.TraceIdGenerator; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.jupiter.api.Assertions; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestInvocation { @Mocked Endpoint endpoint; @Mocked OperationMeta operationMeta; @Mocked InvocationRuntimeType invocationRuntimeType; static long nanoTime = 123; @BeforeClass public static void classSetup() { EventManager.eventBus = new EventBus(); } protected static void mockNonaTime() { new MockUp() { @Mock long nanoTime() { return nanoTime; } }; } @AfterClass public static void classTeardown() { EventManager.eventBus = new EventBus(); } @Test public void onStart() { Holder result = new Holder<>(); Object subscriber = new Object() { @Subscribe public void onStart(InvocationStartEvent event) { result.value = event.getInvocation(); } }; EventManager.register(subscriber); Invocation invocation = new Invocation(endpoint, operationMeta, null); invocation.onStart(); Assertions.assertSame(invocation, result.value); EventManager.unregister(subscriber); } @Test public void onFinish() { mockNonaTime(); Holder result = new Holder<>(); Object subscriber = new Object() { @Subscribe public void onStart(InvocationFinishEvent event) { result.value = event; } }; EventManager.register(subscriber); Invocation invocation = new Invocation(endpoint, operationMeta, null); Assertions.assertFalse(invocation.isFinished()); Response response = Response.succResp(null); invocation.onFinish(response); Assertions.assertSame(invocation, result.value.getInvocation()); Assertions.assertSame(response, result.value.getResponse()); Assertions.assertTrue(invocation.isFinished()); // should not post event again InvocationFinishEvent oldEvent = result.value; invocation.onFinish(null); Assertions.assertSame(oldEvent, result.value); EventManager.unregister(subscriber); } @Test public void isConsumer_yes() { Invocation invocation = new Invocation(endpoint, operationMeta, null); Assertions.assertFalse(invocation.isConsumer()); } @Test public void isConsumer_no(@Mocked ReferenceConfig referenceConfig) { Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); Assertions.assertTrue(invocation.isConsumer()); } @Test public void localContext(@Mocked ReferenceConfig referenceConfig) { Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addLocalContext("k", 1); Assertions.assertSame(invocation.getHandlerContext(), invocation.getLocalContext()); Assertions.assertEquals(1, (int) invocation.getLocalContext("k")); } @Test public void traceId_fromContext(@Mocked ReferenceConfig referenceConfig) { Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test public void traceId_consumerCreateTraceId(@Mocked ReferenceConfig referenceConfig) { TraceIdGenerator generator = SPIServiceUtils.getTargetService(TraceIdGenerator.class, BraveTraceIdGenerator.class); new Expectations(generator) { { generator.generate(); result = "abc"; } }; Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test public void traceId_fromRequest(@Mocked Endpoint endpoint, @Mocked HttpServletRequestEx requestEx) { new Expectations() { { requestEx.getHeader(CoreConst.TRACE_ID_NAME); result = "abc"; } }; Invocation invocation = new Invocation(endpoint, operationMeta, null); invocation.onStart(requestEx); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test public void traceId_producerCreateTraceId(@Mocked Endpoint endpoint, @Mocked HttpServletRequestEx requestEx) { TraceIdGenerator generator = SPIServiceUtils.getTargetService(TraceIdGenerator.class, BraveTraceIdGenerator.class); new Expectations(generator) { { generator.generate(); result = "abc"; } }; Invocation invocation = new Invocation(endpoint, operationMeta, null); invocation.onStart(requestEx); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); } @Test public void traceIdGeneratorInit(@Mocked TraceIdGenerator gen1, @Mocked TraceIdGenerator gen2, @Mocked TraceIdGenerator gen3, @Mocked TraceIdGenerator gen4) { new Expectations(SPIServiceUtils.class) { { gen1.getName(); result = "zipkin"; gen3.getName(); result = "apm"; gen2.getName(); result = "zipkin"; gen4.getName(); result = "apm"; SPIServiceUtils.getOrLoadSortedService(TraceIdGenerator.class); result = Arrays.asList(gen1, gen3, gen2, gen4); } }; MatcherAssert.assertThat(Invocation.loadTraceIdGenerators(), Matchers.containsInAnyOrder(gen1, gen3)); } InvocationBaseEvent invocationBaseEvent; @Test public void test_business_execute_time_correct() { Object listener = new Object() { @Subscribe public void onBusinessMethodStart(InvocationBusinessMethodStartEvent event) { invocationBaseEvent = event; } }; EventManager.getEventBus().register(listener); Invocation invocation = new Invocation(endpoint, operationMeta, null); mockNonaTime(); invocation.onBusinessMethodStart(); EventManager.getEventBus().unregister(listener); Assertions.assertSame(invocation, invocationBaseEvent.getInvocation()); nanoTime++; invocation.onBusinessFinish(); Assertions.assertEquals(1, invocation.getInvocationStageTrace().calcBusinessExecute()); } @Test public void marker(@Mocked ReferenceConfig referenceConfig) { Invocation.INVOCATION_ID.set(0); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestSCBApplicationListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.context.event.ContextClosedEvent; import org.springframework.core.env.Environment; public class TestSCBApplicationListener { @BeforeEach public void before() { } @AfterAll public static void classTeardown() { } @Test public void onApplicationEvent_close() { ContextClosedEvent contextClosedEvent = Mockito.mock(ContextClosedEvent.class); Environment environment = Mockito.mock(Environment.class); SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest(environment); scbEngine.setEnvironment(environment); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); scbEngine.setStatus(SCBStatus.UP); SCBApplicationListener listener = new SCBApplicationListener(scbEngine); listener.onApplicationEvent(contextClosedEvent); Assertions.assertEquals(SCBStatus.DOWN, scbEngine.getStatus()); scbEngine.destroy(); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/TestTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.core.env.Environment; public class TestTransport { @BeforeAll public static void classSetup() { } @AfterAll public static void classTeardown() { } @Test public void testEndpoint() throws Exception { Endpoint oEndpoint = new Endpoint(new Transport() { @Override public Object parseAddress(String address) { return "127.0.0.1"; } @Override public void setEnvironment(Environment environment) { } @Override public boolean init() { return true; } @Override public String getName() { return "test"; } @Override public Endpoint getEndpoint() { return (new Endpoint(this, "testEndpoint")); } @Override public Endpoint getPublishEndpoint() { return (new Endpoint(this, "testEndpoint")); } }, "rest://127.0.0.1:8080"); oEndpoint.getTransport().init(); Assertions.assertEquals("rest://127.0.0.1:8080", oEndpoint.getEndpoint()); Assertions.assertEquals("127.0.0.1", oEndpoint.getAddress()); Assertions.assertEquals("test", oEndpoint.getTransport().getName()); Assertions.assertEquals("rest://127.0.0.1:8080", oEndpoint.getEndpoint()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/consumer/TestReactiveResponseExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.consumer; import org.apache.servicecomb.core.provider.consumer.ReactiveResponseExecutor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestReactiveResponseExecutor { @Test public void testReactiveResponseExecutor() { ReactiveResponseExecutor executor = new ReactiveResponseExecutor(); Runnable cmd = Mockito.mock(Runnable.class); boolean validAssert = true; try { executor.execute(cmd); } catch (Exception e) { validAssert = false; } Assertions.assertTrue(validAssert); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/consumer/TestSyncResponseExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.consumer; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.provider.consumer.SyncResponseExecutor; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestSyncResponseExecutor { @Test public void testSyncResponseExecutor() { SyncResponseExecutor executor = new SyncResponseExecutor(); Runnable cmd = Mockito.mock(Runnable.class); Response response = Mockito.mock(Response.class); Invocation invocation = Mockito.mock(Invocation.class); executor.execute(cmd); executor.setResponse(response); try { Response responseValue = executor.waitResponse(invocation); Assertions.assertNotNull(responseValue); } catch (Exception e) { Assertions.assertNotNull(e); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/definition/MicroServicePropertyExtendedStub.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import java.util.Map; import org.apache.servicecomb.registry.api.PropertyExtended; public class MicroServicePropertyExtendedStub implements PropertyExtended { @Override public Map getExtendedProperties() { return null; } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/definition/OperationConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.CoreConst; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class OperationConfigTest { OperationConfig config = new OperationConfig(); @Nested class InPoolTimeout { @Test void should_get_rest_value() { config.setMsDefaultRequestWaitInPoolTimeout(1); config.setMsRestRequestWaitInPoolTimeout(2); long nano = TimeUnit.MILLISECONDS.toNanos(2); assertThat(config.getNanoRestRequestWaitInPoolTimeout()).isEqualTo(nano); assertThat(config.getNanoRequestWaitInPoolTimeout(CoreConst.RESTFUL)).isEqualTo(nano); } @Test void should_get_highway_value() { config.setMsDefaultRequestWaitInPoolTimeout(1); config.setMsHighwayRequestWaitInPoolTimeout(2); long nano = TimeUnit.MILLISECONDS.toNanos(2); assertThat(config.getNanoHighwayRequestWaitInPoolTimeout()).isEqualTo(nano); assertThat(config.getNanoRequestWaitInPoolTimeout(CoreConst.HIGHWAY)).isEqualTo(nano); } @Test void should_support_register_customize_transport() { config.setMsDefaultRequestWaitInPoolTimeout(1); config.registerRequestWaitInPoolTimeout("abc", 2); long nano = TimeUnit.MILLISECONDS.toNanos(2); assertThat(config.getNanoRequestWaitInPoolTimeout("abc")).isEqualTo(nano); } @Test void should_get_invocation_timeout_value() { config.setMsInvocationTimeout(1); long nano = TimeUnit.MILLISECONDS.toNanos(1); assertThat(config.getNanoInvocationTimeout()).isEqualTo(nano); assertThat(config.getMsInvocationTimeout()).isEqualTo(1); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/definition/TestMicroserviceMetaManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.definition; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestMicroserviceMetaManager { @Test public void testEnsureFindSchemaMeta() { SchemaMeta meta = Mockito.mock(SchemaMeta.class); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); Mockito.when(microserviceMeta.ensureFindSchemaMeta("yhfghj")).thenReturn(meta); Assertions.assertEquals(meta, microserviceMeta.ensureFindSchemaMeta("yhfghj")); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/event/TestInvocationStartEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.event; import org.apache.servicecomb.core.Invocation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestInvocationStartEvent { InvocationStartEvent event; @Test public void construct() { Invocation invocation = Mockito.mock(Invocation.class); event = new InvocationStartEvent(invocation); Assertions.assertSame(invocation, event.getInvocation()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collections; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; import jakarta.ws.rs.core.Response.StatusType; class ExceptionsTest { private static final ObjectMapper objectMapper = new ObjectMapper(); @Test void should_not_convert_invocation_exception() { InvocationException exception = Exceptions.genericConsumer("msg"); assertThat(Exceptions.convert(null, exception, BAD_REQUEST)) .isSameAs(exception); } @Test void should_convert_unknown_client_exception_to_invocation_exception() { IllegalStateException exception = new IllegalStateException("msg"); InvocationException invocationException = Exceptions.convert(null, exception, BAD_REQUEST); assertThat(invocationException).hasCause(exception); assertThat(invocationException.getStatus()).isEqualTo(BAD_REQUEST); assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); try { assertThat(objectMapper.readTree(Json.encode(invocationException.getErrorData()))) .isEqualTo(objectMapper.readTree("{\"code\":\"SCB.00000000\",\"message\":\"Unexpected exception when processing none. msg\"}")); } catch (Exception e) { throw new AssertionError("Error during JSON comparison: " + e.getMessage(), e); } } @Test void should_convert_unknown_server_exception_to_invocation_exception() { IllegalStateException exception = new IllegalStateException("msg"); InvocationException invocationException = Exceptions.convert(null, exception, INTERNAL_SERVER_ERROR); assertThat(invocationException).hasCause(exception); assertThat(invocationException.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); try { assertThat(objectMapper.readTree(Json.encode(invocationException.getErrorData()))) .isEqualTo(objectMapper.readTree("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected exception when processing none. msg\"}")); } catch (Exception e) { throw new AssertionError("Error during JSON comparison: " + e.getMessage(), e); } } static class ThrowExceptionWhenConvert implements ExceptionConverter { @Override public int getOrder() { return Integer.MIN_VALUE; } @Override public boolean canConvert(Throwable throwable) { return true; } @Override public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { throw new RuntimeExceptionWithoutStackTrace("mock exception when convert"); } } @Test void should_protect_when_converter_throw_exception() { DefaultExceptionProcessor processor = new DefaultExceptionProcessor() .setConverters(Collections.singletonList(new ThrowExceptionWhenConvert())); try (LogCollector logCollector = new LogCollector()) { InvocationException exception = processor .convert(null, new RuntimeExceptionWithoutStackTrace("exception need convert"), BAD_REQUEST); assertThat(exception.getStatus()) .isSameAs(INTERNAL_SERVER_ERROR); assertThat(exception.getErrorData().toString()) .isEqualTo("CommonExceptionData{code='SCB.50000000', message='Internal Server Error', dynamic={}}"); assertThat(logCollector.getLastEvents().getMessage().getFormattedMessage().replace("\r\n", "\n")) .isEqualTo("BUG: ExceptionConverter.convert MUST not throw exception, please fix it.\n" + "original exception :org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace: exception need convert\n" + "converter exception:org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace: mock exception when convert\n"); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.exception.converter; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.TimeoutException; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.Test; class TimeoutExceptionConverterTest { private InvocationException convert() { TimeoutException timeoutException = new TimeoutException( "The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080"); return Exceptions.convert(null, timeoutException, Status.BAD_REQUEST); } @Test void should_not_leak_server_ip() { InvocationException exception = convert(); assertThat(exception) .hasMessage( "InvocationException: code=408;msg=CommonExceptionData{code='SCB.00000000', message='Request Timeout.', dynamic={}}"); } @Test void should_log_detail() { try (LogCollector logCollector = new LogCollector()) { convert(); assertThat(logCollector.getLastEvents().getMessage().getFormattedMessage()) .isEqualTo( "Request timeout, Details: The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080."); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/executor/TestExecutorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.util.concurrent.Executor; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import mockit.Expectations; import mockit.Mocked; public class TestExecutorManager { @Mocked Executor defaultExecutor; ExecutorManager executorManager = new ExecutorManager(); Environment environment = Mockito.mock(Environment.class); @Before public void setup() { executorManager.setEnvironment(environment); } @After public void teardown() { } @Test public void findExecutor_oneParam(@Mocked Executor executor, @Mocked OperationMeta operationMeta) { new Expectations(BeanUtils.class) { { BeanUtils.getBean(ExecutorManager.EXECUTOR_DEFAULT); result = executor; } }; Assertions.assertSame(executor, executorManager.findExecutor(operationMeta)); } @Test public void findExecutor_twoParam_opCfg_withoutOpDef(@Mocked Executor executor, @Mocked OperationMeta operationMeta) { // String schemaQualifiedName = "schemaId.opId"; String microserviceQualifiedName = "microserviceName.schemaId.opId"; String opBeanId = "opBeanId"; Mockito.when(environment.getProperty( ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName)).thenReturn(opBeanId); new Expectations(BeanUtils.class) { { operationMeta.getMicroserviceQualifiedName(); result = microserviceQualifiedName; BeanUtils.getBean(opBeanId); result = executor; } }; Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); } @Test public void findExecutor_twoParam_opCfg_withOpDef(@Mocked Executor executor, @Mocked Executor defExecutor, @Mocked OperationMeta operationMeta) { String microserviceQualifiedName = "microserviceName.schemaId.opId"; String opBeanId = "opBeanId"; Mockito.when(environment.getProperty( ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName)).thenReturn(opBeanId); new Expectations(BeanUtils.class) { { operationMeta.getMicroserviceQualifiedName(); result = microserviceQualifiedName; BeanUtils.getBean(opBeanId); result = executor; } }; Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, defExecutor)); } @Test public void findExecutor_twoParam_schemaCfg_withOpDef(@Mocked OperationMeta operationMeta, @Mocked Executor defExecutor) { Assertions.assertSame(defExecutor, executorManager.findExecutor(operationMeta, defExecutor)); } @Test public void findExecutor_twoParam_schemaCfg_withoutOpDef(@Mocked Executor executor, @Mocked OperationMeta operationMeta) { String microserviceName = "serviceName"; String schemaName = "schemaId"; String opBeanId = "opBeanId"; Mockito.when(environment.getProperty(ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceName + "." + schemaName)) .thenReturn(opBeanId); new Expectations(BeanUtils.class) { { operationMeta.getSchemaId(); result = schemaName; operationMeta.getMicroserviceName(); result = microserviceName; BeanUtils.getBean(opBeanId); result = executor; } }; Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); } @Test public void findExecutor_twoParam_defaultCfg(@Mocked Executor executor, @Mocked SchemaMeta schemaMeta, @Mocked OperationMeta operationMeta) { String beanId = "beanId"; Mockito.when(environment.getProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT)).thenReturn(beanId); new Expectations(BeanUtils.class) { { BeanUtils.getBean(beanId); result = executor; } }; Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_CORE_THREADS; import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_GROUP; import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_MAX_IDLE_SECOND; import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_MAX_QUEUE_SIZE; import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_MAX_THREADS; import static org.apache.servicecomb.core.executor.GroupExecutor.KEY_OLD_MAX_THREAD; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import mockit.Deencapsulation; public class TestGroupExecutor { String strThreadTest = "default"; @BeforeEach public void setup() { Mockito.when(environment.getProperty(KEY_GROUP, int.class, 2)).thenReturn(2); Mockito.when(environment.getProperty(KEY_CORE_THREADS, int.class, 25)).thenReturn(25); Mockito.when(environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60)).thenReturn(60); Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty(KEY_OLD_MAX_THREAD, int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE)) .thenReturn(Integer.MAX_VALUE); } @AfterAll public static void teardown() { } Environment environment = Mockito.mock(Environment.class); GroupExecutor groupExecutor = new GroupExecutor(environment); @Test public void groupCount() { groupExecutor.initConfig(); Assertions.assertEquals(2, groupExecutor.groupCount); Mockito.when(environment.getProperty(KEY_GROUP, int.class, 2)).thenReturn(4); groupExecutor.initConfig(); Assertions.assertEquals(4, groupExecutor.groupCount); } @Test public void coreThreads() { groupExecutor.initConfig(); Assertions.assertEquals(25, groupExecutor.coreThreads); Mockito.when(environment.getProperty(KEY_CORE_THREADS, int.class, 25)).thenReturn(100); groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.coreThreads); } @Test public void maxIdleInSecond() { groupExecutor.initConfig(); Assertions.assertEquals(60, groupExecutor.maxIdleInSecond); Mockito.when(environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60)).thenReturn(100); groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.maxIdleInSecond); } @Test public void maxQueueSize() { groupExecutor.initConfig(); Assertions.assertEquals(Integer.MAX_VALUE, groupExecutor.maxQueueSize); Mockito.when(environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE)).thenReturn(100); groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.maxQueueSize); } @Test public void maxThreads() { groupExecutor.initConfig(); Assertions.assertEquals(100, groupExecutor.maxThreads); LogCollector collector = new LogCollector(); Mockito.when(environment.getProperty(KEY_OLD_MAX_THREAD, int.class, -1)).thenReturn(200); groupExecutor.initConfig(); Assertions.assertEquals(200, groupExecutor.maxThreads); Assertions.assertEquals( "servicecomb.executor.default.thread-per-group is deprecated, recommended to use servicecomb.executor.default.maxThreads-per-group.", collector.getEvents().get(collector.getEvents().size() - 2).getMessage().getFormattedMessage()); collector.tearDown(); Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(300); groupExecutor.initConfig(); Assertions.assertEquals(300, groupExecutor.maxThreads); } @Test public void adjustCoreThreads() { Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(10); LogCollector collector = new LogCollector(); groupExecutor.initConfig(); Assertions.assertEquals(10, groupExecutor.maxThreads); Assertions.assertEquals( "coreThreads is bigger than maxThreads, change from 25 to 10.", collector.getEvents().get(collector.getEvents().size() - 2).getMessage().getFormattedMessage()); collector.tearDown(); } @Test public void testGroupExecutor() { groupExecutor.init(); groupExecutor.execute(() -> { }); Map threadExecutorMap = Deencapsulation.getField(groupExecutor, "threadExecutorMap"); Assertions.assertTrue((threadExecutorMap.size() > 0)); List executorList = Deencapsulation.getField(groupExecutor, "executorList"); Assertions.assertTrue((executorList.size() > 1)); ReactiveExecutor oReactiveExecutor = new ReactiveExecutor(); oReactiveExecutor.execute(() -> strThreadTest = "thread Ran"); oReactiveExecutor.close(); Assertions.assertEquals("thread Ran", strThreadTest); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/executor/TestThreadPoolExecutorEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.executor; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.IntSupplier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestThreadPoolExecutorEx { private static final Logger LOGGER = LoggerFactory.getLogger(TestThreadPoolExecutorEx.class); static class TestTask implements Runnable { CountDownLatch notify = new CountDownLatch(1); Future future; public void quit() throws ExecutionException, InterruptedException { notify.countDown(); future.get(); } @Override public void run() { try { notify.await(); } catch (InterruptedException e) { throw new IllegalStateException(e); } } } ThreadPoolExecutorEx executorEx = new ThreadPoolExecutorEx(2, 4, 2, TimeUnit.SECONDS, new LinkedBlockingQueueEx(2), Executors.defaultThreadFactory()); public TestTask submitTask() { TestTask task = new TestTask(); task.future = executorEx.submit(task); return task; } @Test public void schedule() throws ExecutionException, InterruptedException { // init Assertions.assertEquals(0, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(0, executorEx.getNotFinished()); Assertions.assertEquals(0, executorEx.getQueue().size()); // use core threads TestTask t1 = submitTask(); Assertions.assertEquals(1, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(1, executorEx.getNotFinished()); Assertions.assertEquals(0, executorEx.getQueue().size()); TestTask t2 = submitTask(); Assertions.assertEquals(2, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(2, executorEx.getNotFinished()); Assertions.assertEquals(0, executorEx.getQueue().size()); // extend threads TestTask t3 = submitTask(); Assertions.assertEquals(3, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(3, executorEx.getNotFinished()); Assertions.assertEquals(0, executorEx.getQueue().size()); TestTask t4 = submitTask(); Assertions.assertEquals(4, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(4, executorEx.getNotFinished()); Assertions.assertEquals(0, executorEx.getQueue().size()); // queue the tasks TestTask t5 = submitTask(); Assertions.assertEquals(4, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(5, executorEx.getNotFinished()); Assertions.assertEquals(1, executorEx.getQueue().size()); TestTask t6 = submitTask(); Assertions.assertEquals(4, executorEx.getPoolSize()); Assertions.assertEquals(0, executorEx.getRejectedCount()); Assertions.assertEquals(6, executorEx.getNotFinished()); Assertions.assertEquals(2, executorEx.getQueue().size()); // reject the task try { submitTask(); } catch (RejectedExecutionException e) { } Assertions.assertEquals(4, executorEx.getPoolSize()); Assertions.assertEquals(1, executorEx.getRejectedCount()); Assertions.assertEquals(6, executorEx.getNotFinished()); Assertions.assertEquals(2, executorEx.getQueue().size()); // t1/t2/t3 finish t1.quit(); t2.quit(); t3.quit(); Assertions.assertEquals(4, executorEx.getPoolSize()); Assertions.assertEquals(1, executorEx.getRejectedCount()); waitForResult(3, executorEx::getNotFinished); waitForResult(0, executorEx.getQueue()::size); // reuse thread t3 = submitTask(); Assertions.assertEquals(4, executorEx.getPoolSize()); Assertions.assertEquals(1, executorEx.getRejectedCount()); waitForResult(4, executorEx::getNotFinished); waitForResult(0, executorEx.getQueue()::size); t3.quit(); t4.quit(); t5.quit(); t6.quit(); waitForResult(2, executorEx::getPoolSize); executorEx.shutdown(); } private void waitForResult(int expect, IntSupplier supplier) { long max = 30000; long waited = 0; for (; ; ) { if (waited > max) { throw new IllegalStateException("timed out waiting."); } int actual = supplier.getAsInt(); if (expect == actual) { return; } LOGGER.info("waiting for thread result, expect:{}, actual: {}.", expect, actual); try { TimeUnit.MILLISECONDS.sleep(200); waited += 200; } catch (InterruptedException e) { throw new IllegalStateException(e); } } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter; import static org.apache.servicecomb.core.filter.FilterNode.buildChain; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.util.List; import java.util.Vector; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationConfig; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.filter.impl.ScheduleFilter; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.BeforeClass; import org.junit.Test; import mockit.Expectations; import mockit.Mocked; public class FilterChainTest { static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(); static String THREAD_NAME; @Mocked Invocation invocation; @Mocked OperationMeta operationMeta; @Mocked OperationConfig operationConfig; List msg = new Vector<>(); Filter recordThreadFilter = new AbstractFilter() { @Override public String getName() { return "f2"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { msg.add(Thread.currentThread().getName()); if (nextNode == null) { return CompletableFuture.completedFuture(Response.ok(null)); } return nextNode.onFilter(invocation); } }; Filter scheduler = new ScheduleFilter(); Filter exceptionFilter = new AbstractFilter() { @Override public String getName() { return "f1"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { throw new IllegalStateException("e1"); } }; @BeforeClass public static void beforeClass() { try { THREAD_NAME = EXECUTOR.submit(() -> Thread.currentThread().getName()).get(); } catch (Exception e) { throw new IllegalStateException(e); } } private void mockInvocation() { new Expectations() { { invocation.getOperationMeta(); result = operationMeta; operationMeta.getExecutor(); result = EXECUTOR; operationConfig.getNanoRequestWaitInPoolTimeout(anyString); result = Long.MAX_VALUE; } }; } @Test public void should_switch_thread_after_schedule() throws ExecutionException, InterruptedException { mockInvocation(); buildChain(recordThreadFilter, scheduler, recordThreadFilter) .onFilter(invocation) .get(); assertThat(msg).containsExactly("main", THREAD_NAME); } @Test public void should_stop_chain_when_first_filter_throw_exception() { ExecutionException executionException = (ExecutionException) catchThrowable( () -> buildChain(exceptionFilter, recordThreadFilter) .onFilter(invocation) .get()); assertThat(msg).isEmpty(); assertThat(executionException.getCause()) .isInstanceOf(IllegalStateException.class) .hasMessage("e1"); } @Test public void should_stop_chain_when_middle_filter_throw_exception() { ExecutionException executionException = (ExecutionException) catchThrowable( () -> buildChain(recordThreadFilter, exceptionFilter, recordThreadFilter) .onFilter(invocation) .get()); assertThat(msg).containsExactly("main"); assertThat(executionException.getCause()) .isInstanceOf(IllegalStateException.class) .hasMessage("e1"); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.servicecomb.core.exception.ExceptionCodes.DEFAULT_VALIDATE; import static org.apache.servicecomb.core.exception.converter.ConstraintViolationExceptionConverter.KEY_CODE; import static org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter.ENABLE_EL; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.util.List; import org.apache.commons.lang3.reflect.MethodUtils; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.exception.converter.ConstraintViolationExceptionConverter; import org.apache.servicecomb.core.exception.converter.ValidateDetail; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MutablePropertySources; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import mockit.Expectations; import mockit.Mocked; public class ParameterValidatorFilterTest { public static class BaseModel { @NotNull(message = "can not be null") private String name; public String getName() { return name; } public BaseModel setName(String name) { this.name = name; return this; } } public static class Model extends BaseModel { @NotBlank(message = "can not be blank") @JsonProperty(value = "nick-name") private String nickName; public String getNickName() { return nickName; } public Model setNickName(String nickName) { this.nickName = nickName; return this; } } public static class Controller { public void op(@NotNull(message = "not null") String query, @Valid Model model) { } } ParameterValidatorFilter filter = new ParameterValidatorFilter(); @Mocked Invocation invocation; @Mocked SwaggerProducerOperation operation; ConfigurableEnvironment environment; @Before public void setUp() throws Exception { environment = Mockito.mock(ConfigurableEnvironment.class); SCBEngine engine = SCBBootstrap.createSCBEngineForTest(environment); MutablePropertySources sources = new MutablePropertySources(); Mockito.when(environment.getProperty(ENABLE_EL, boolean.class, false)).thenReturn(false); Mockito.when(environment.getPropertySources()).thenReturn(sources); filter.setEnvironment(environment); filter.afterPropertiesSet(); engine.setEnvironment(environment); new Expectations() { { operation.getProducerInstance(); result = new Controller(); operation.getProducerMethod(); result = MethodUtils.getAccessibleMethod(Controller.class, "op", String.class, Model.class); invocation.toProducerArguments(); result = new Object[] {null, new Model()}; } }; } private InvocationException getException() { Throwable throwable = catchThrowable(() -> filter.onFilter(invocation, FilterNode.EMPTY).get()); return Exceptions.convert(invocation, throwable, BAD_REQUEST); } private CommonExceptionData getExceptionData() { InvocationException invocationException = getException(); return (CommonExceptionData) invocationException.getErrorData(); } @Test public void status_code_should_be_bad_request() { InvocationException invocationException = getException(); assertThat(invocationException.getStatusCode()).isEqualTo(BAD_REQUEST.getStatusCode()); } @Test public void error_code_and_message_should_be_build_in_value() { Mockito.when(environment.getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE)).thenReturn(DEFAULT_VALIDATE); CommonExceptionData errorData = getExceptionData(); assertThat(errorData.getCode()).isEqualTo(DEFAULT_VALIDATE); assertThat(errorData.getMessage()).isEqualTo("invalid parameters."); } @Test public void should_allow_customize_error_code_by_configuration() { Mockito.when(environment.getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE)).thenReturn("test.0001"); SPIServiceUtils.getTargetService(ExceptionConverter.class, ConstraintViolationExceptionConverter.class); CommonExceptionData errorData = getExceptionData(); assertThat(errorData.getCode()).isEqualTo("test.0001"); } @Test public void should_use_json_property_value_as_property_name() { CommonExceptionData errorData = getExceptionData(); List details = errorData.getDynamic("validateDetail"); assertThat(details.stream().map(ValidateDetail::getPropertyPath)) .contains("op.model.nick-name"); } @Test public void should_include_all_validate_detail() { CommonExceptionData errorData = getExceptionData(); List details = errorData.getDynamic("validateDetail"); assertThat(details.stream().map(ValidateDetail::getPropertyPath)) .contains("op.query", "op.model.name", "op.model.nick-name"); assertThat(details.stream().map(ValidateDetail::getMessage)) .contains("not null", "can not be null", "can not be blank"); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.filter.impl; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.lang.reflect.Method; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.Test; import mockit.Expectations; import mockit.Injectable; import mockit.Mocked; import mockit.Verifications; public class ProducerOperationFilterTest { ProviderOperationFilter filter = new ProviderOperationFilter(); @Injectable Invocation invocation; @Mocked SwaggerProducerOperation producerOperation; static InvocationContext threadInvocationContext; public static class Controller { public void sync() { threadInvocationContext = ContextUtils.getInvocationContext(); } public void syncException() { throw new RuntimeExceptionWithoutStackTrace("syncException"); } public CompletableFuture async() { threadInvocationContext = ContextUtils.getInvocationContext(); return CompletableFuture.completedFuture(null); } public CompletableFuture asyncException() { throw new RuntimeExceptionWithoutStackTrace("asyncException"); } } @Test public void should_record_invocation_trace_time() throws NoSuchMethodException { setInvokeSyncMethod(); filter.onFilter(invocation, FilterNode.EMPTY); new Verifications() { { invocation.onBusinessMethodStart(); times = 1; invocation.onBusinessFinish(); times = 1; } }; } private void setInvokeMethod(String name) throws NoSuchMethodException { Controller instance = new Controller(); Method method = instance.getClass().getMethod(name); new Expectations() { { producerOperation.getProducerInstance(); result = instance; producerOperation.getProducerMethod(); result = method; } }; } private void setInvokeSyncMethod() throws NoSuchMethodException { setInvokeMethod("sync"); } private void setInvokeAsyncMethod() throws NoSuchMethodException { setInvokeMethod("async"); } @Test public void should_provide_thread_local_invocation_context_for_sync_method() throws NoSuchMethodException { setInvokeSyncMethod(); filter.onFilter(invocation, FilterNode.EMPTY); assertThat(threadInvocationContext).isSameAs(invocation); } @Test public void should_clear_thread_local_invocation_context_after_sync_method() throws NoSuchMethodException { setInvokeSyncMethod(); filter.onFilter(invocation, FilterNode.EMPTY); assertThat(ContextUtils.getInvocationContext()).isNull(); } @Test public void should_catch_sync_business_exception() throws NoSuchMethodException { setInvokeMethod("syncException"); CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) .failsWithin(Duration.ofSeconds(1)) .withThrowableOfType(ExecutionException.class) .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) .withMessage("org.apache.servicecomb.foundation.test.scaffolding.exception" + ".RuntimeExceptionWithoutStackTrace: syncException"); } @Test public void should_provide_thread_local_invocation_context_for_async_method() throws NoSuchMethodException { setInvokeAsyncMethod(); filter.onFilter(invocation, FilterNode.EMPTY); assertThat(threadInvocationContext).isSameAs(invocation); } @Test public void should_clear_thread_local_invocation_context_after_async_method() throws NoSuchMethodException { setInvokeAsyncMethod(); filter.onFilter(invocation, FilterNode.EMPTY); assertThat(ContextUtils.getInvocationContext()).isNull(); } @Test public void should_catch_async_business_exception() throws NoSuchMethodException { setInvokeMethod("asyncException"); CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) .failsWithin(Duration.ofSeconds(1)) .withThrowableOfType(ExecutionException.class) .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) .withMessage("org.apache.servicecomb.foundation.test.scaffolding" + ".exception.RuntimeExceptionWithoutStackTrace: asyncException"); } @Test public void should_unify_IllegalArgumentException_message_when_convert_exception() throws NoSuchMethodException { setInvokeSyncMethod(); new Expectations() { { invocation.toProducerArguments(); result = new Object[] {1}; } }; CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) .failsWithin(Duration.ofSeconds(1)) .withThrowableOfType(ExecutionException.class) .withCauseExactlyInstanceOf(IllegalArgumentException.class); InvocationException throwable = Exceptions .convert(invocation, catchThrowable(future::get), INTERNAL_SERVER_ERROR); assertThat(throwable).hasCauseInstanceOf(IllegalArgumentException.class); CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); assertThat(data.getMessage()).isEqualTo("Parameters not valid or types not match."); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.Test; import mockit.Expectations; import mockit.Injectable; import mockit.Mocked; import mockit.Verifications; public class ProducerInvocationFlowTest { class TestFlow extends ProducerInvocationFlow { public TestFlow(InvocationCreator invocationCreator) { super(invocationCreator); } @Override protected Invocation sendCreateInvocationException(Throwable throwable) { sendException = Exceptions.unwrap(throwable); return null; } @Override protected void endResponse(Invocation invocation, Response response) { sendInvocation = invocation; } } FilterNode filterNode = FilterNode.EMPTY; Throwable sendException; Invocation sendInvocation; @Injectable Invocation invocation; @Mocked MicroserviceMeta microserviceMeta; @Test public void should_send_exception_response_when_failed_to_create_invocation() { RuntimeException exception = new RuntimeExceptionWithoutStackTrace(); TestFlow flow = new TestFlow(() -> { throw exception; }); flow.run(); assertThat(sendException).isSameAs(exception); } private void mockFilterChain() { new Expectations() { { microserviceMeta.getProviderFilterChain(); result = filterNode; } }; } @Test public void should_start_invocation_when_succeed_to_create_invocation() { mockFilterChain(); TestFlow flow = new TestFlow(() -> completedFuture(invocation)); flow.run(); new Verifications() { { invocation.onStart((HttpServletRequestEx) any); times = 1; } }; } @Test public void should_send_response_when_invocation_success() { mockFilterChain(); TestFlow flow = new TestFlow(() -> completedFuture(invocation)); flow.run(); assertThat(sendInvocation).isSameAs(invocation); } @Test public void should_finish_invocation_when_invocation_success() { mockFilterChain(); TestFlow flow = new TestFlow(() -> completedFuture(invocation)); flow.run(); new Verifications() { { invocation.onFinish((Response) any); times = 1; } }; } private void mockInvocationFailed() { filterNode = new FilterNode(new AbstractFilter() { @Override public String getName() { return "test"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { throw new RuntimeExceptionWithoutStackTrace(); } }); mockFilterChain(); } @Test public void should_send_response_when_invocation_fail() { mockInvocationFailed(); TestFlow flow = new TestFlow(() -> completedFuture(invocation)); flow.run(); assertThat(sendInvocation).isSameAs(invocation); } @Test public void should_finish_invocation_when_invocation_fail() { mockInvocationFailed(); TestFlow flow = new TestFlow(() -> completedFuture(invocation)); flow.run(); new Verifications() { { invocation.onFinish((Response) any); times = 1; } }; } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.junit.BeforeClass; import org.junit.Test; import org.junit.jupiter.api.Assertions; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestInvocationStageTrace { Invocation invocation; InvocationStageTrace stageTrace; @Mocked Endpoint endpoint; @Mocked ReferenceConfig referenceConfig; @Mocked OperationMeta operationMeta; @Mocked InvocationRuntimeType invocationRuntimeType; Map args = new HashMap<>(); static long nanoTime = 0; @BeforeClass public static void classSetup() { new MockUp() { @Mock long nanoTime() { return nanoTime; } }; } @Test public void consumer() { invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); stageTrace = new InvocationStageTrace(invocation); stageTrace.startCreateInvocation(1); nanoTime = 2; stageTrace.finishCreateInvocation(); stageTrace.startConsumerConnection(); nanoTime = 3; stageTrace.finishConsumerConnection(); stageTrace.startConsumerEncodeRequest(); nanoTime = 4; stageTrace.finishConsumerEncodeRequest(); stageTrace.startConsumerSendRequest(); nanoTime = 5; stageTrace.finishConsumerSendRequest(); stageTrace.startWaitResponse(); nanoTime = 6; stageTrace.finishWaitResponse(); stageTrace.startConsumerDecodeResponse(); nanoTime = 7; stageTrace.finishConsumerDecodeResponse(); stageTrace.finish(); Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConnection(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConsumerEncodeRequest(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConsumerSendRequest(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcWait(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConsumerDecodeResponse(), 0.1f); Assertions.assertEquals(6f, stageTrace.calcTotal(), 0.1f); } @Test public void producer() { invocation = new Invocation(endpoint, operationMeta, args); stageTrace = new InvocationStageTrace(invocation); stageTrace.startCreateInvocation(1); nanoTime = 2; stageTrace.finishCreateInvocation(); stageTrace.startProviderDecodeRequest(); nanoTime = 3; stageTrace.finishProviderDecodeRequest(); stageTrace.startProviderQueue(); nanoTime = 4; stageTrace.finishProviderQueue(); stageTrace.startBusinessExecute(); nanoTime = 5; stageTrace.finishBusinessExecute(); stageTrace.startProviderEncodeResponse(); nanoTime = 6; stageTrace.finishProviderEncodeResponse(); stageTrace.startProviderSendResponse(); nanoTime = 7; stageTrace.finishProviderSendResponse(); stageTrace.finish(); Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcQueue(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcProviderDecodeRequest(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcProviderEncodeResponse(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcBusinessExecute(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcProviderSendResponse(), 0.1f); Assertions.assertEquals(6f, stageTrace.calcTotal(), 0.1f); } @Test public void edge() { invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); stageTrace = new InvocationStageTrace(invocation); invocation.setEdge(); stageTrace.startCreateInvocation(1); nanoTime = 2; stageTrace.finishCreateInvocation(); stageTrace.startProviderDecodeRequest(); nanoTime = 3; stageTrace.finishProviderDecodeRequest(); stageTrace.startConsumerConnection(); nanoTime = 4; stageTrace.finishConsumerConnection(); stageTrace.startConsumerEncodeRequest(); nanoTime = 5; stageTrace.finishConsumerEncodeRequest(); stageTrace.startConsumerSendRequest(); nanoTime = 6; stageTrace.finishConsumerSendRequest(); stageTrace.startWaitResponse(); nanoTime = 7; stageTrace.finishWaitResponse(); stageTrace.startConsumerDecodeResponse(); nanoTime = 8; stageTrace.finishConsumerDecodeResponse(); stageTrace.startProviderEncodeResponse(); nanoTime = 10; stageTrace.finishProviderEncodeResponse(); stageTrace.startProviderSendResponse(); nanoTime = 11; stageTrace.finishProviderSendResponse(); stageTrace.finish(); Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConnection(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConsumerEncodeRequest(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConsumerSendRequest(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcWait(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcConsumerDecodeResponse(), 0.1f); Assertions.assertEquals(2f, stageTrace.calcProviderEncodeResponse(), 0.1f); Assertions.assertEquals(1f, stageTrace.calcProviderSendResponse(), 0.1f); Assertions.assertEquals(10f, stageTrace.calcTotal(), 0.1f); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import static com.google.common.collect.ImmutableMap.of; import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.swagger.v3.oas.models.OpenAPI; public class EndpointTest { public interface TestSchema { void say(Endpoint endpoint); } @Test void should_ignore_endpoint_when_generate_swagger() { SwaggerGenerator generator = SwaggerGenerator.create(TestSchema.class); OpenAPI swagger = generator.generate(); assertThat(swagger.getInfo().getTitle()).contains("EndpointTest$TestSchema"); assertThat(swagger.getPaths()).containsKey("/say"); assertThat(swagger.getPaths().get("/say").getPost().getOperationId()).isEqualTo("say"); assertThat(swagger.getPaths().get("/say").getPost().getResponses()).containsKey("200"); } @Test void should_set_endpoint_to_invocation_when_map_arguments() { SwaggerEnvironment environment = new SwaggerEnvironment(); SwaggerConsumer consumer = environment .createConsumer(TestSchema.class, SwaggerGenerator.generate(TestSchema.class)); SwaggerConsumerOperation operation = consumer.findOperation("say"); Endpoint endpoint = new Endpoint(Mockito.mock(Transport.class), null); Invocation invocation = Mockito.mock(Invocation.class); Holder holder = new Holder<>(); Mockito .doAnswer(invocationOnMock -> { holder.value = invocationOnMock.getArguments()[0]; return null; }) .when(invocation) .setEndpoint(Mockito.any()); Map argsMap = of("endpoint", endpoint); operation.getArgumentsMapper().invocationArgumentToSwaggerArguments(invocation, argsMap); assertThat(holder.value).isSameAs(endpoint); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.endpoint; import static org.apache.servicecomb.core.invocation.endpoint.EndpointUtils.formatFromUri; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class EndpointUtilsTest { @Nested class Http { @Test void should_convert_without_port() { assertThat(formatFromUri("http://host")).isEqualTo("rest://host:80"); } @Test void should_convert_with_port() { assertThat(formatFromUri("http://host:8080")).isEqualTo("rest://host:8080"); } @Test void should_not_lost_query_parameters() { assertThat(formatFromUri("http://host?q1=v1&q2=v2")).isEqualTo("rest://host:80?q1=v1&q2=v2"); } } @Nested class Https { @Test void should_convert_without_port() { assertThat(formatFromUri("https://host")).isEqualTo("rest://host:443?sslEnabled=true"); } @Test void should_convert_with_port() { assertThat(formatFromUri("https://host:8443")).isEqualTo("rest://host:8443?sslEnabled=true"); } @Test void should_not_lost_query_parameters() { assertThat(formatFromUri("https://host?q1=v1&q2=v2")).isEqualTo("rest://host:443?q1=v1&q2=v2&sslEnabled=true"); } } @Nested class H2C { @Test void should_convert_without_port() { assertThat(formatFromUri("h2c://host")).isEqualTo("rest://host:80?protocol=http2"); } @Test void should_convert_with_port() { assertThat(formatFromUri("h2c://host:8080")).isEqualTo("rest://host:8080?protocol=http2"); } @Test void should_not_lost_query_parameters() { assertThat(formatFromUri("h2c://host?q1=v1&q2=v2")).isEqualTo("rest://host:80?q1=v1&q2=v2&protocol=http2"); } } @Nested class H2 { @Test void should_convert_without_port() { assertThat(formatFromUri("h2://host")).isEqualTo("rest://host:443?sslEnabled=true&protocol=http2"); } @Test void should_convert_with_port() { assertThat(formatFromUri("h2://host:8443")).isEqualTo("rest://host:8443?sslEnabled=true&protocol=http2"); } @Test void should_not_lost_query_parameters() { assertThat(formatFromUri("h2://host?q1=v1&q2=v2")) .isEqualTo("rest://host:443?q1=v1&q2=v2&sslEnabled=true&protocol=http2"); } } @Nested class NotProvideScheme { @Test void should_set_scheme_to_h2c() { assertThat(formatFromUri("host")).isEqualTo("rest://host:80?protocol=http2"); } @Test void should_not_lost_query_parameters() { assertThat(formatFromUri("host?q1=v1&q2=v2")).isEqualTo("rest://host:80?q1=v1&q2=v2&protocol=http2"); } } @Nested class UnknownScheme { @Test void should_not_change() { assertThat(formatFromUri("abc://host:123?q1=v1")).isEqualTo("abc://host:123?q1=v1"); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.timeout; import static org.apache.servicecomb.core.invocation.timeout.PassingTimeStrategy.CHAIN_START_TIME; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.test.scaffolding.time.MockClock; import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableMap; class PassingTimeStrategyTest { PassingTimeStrategy strategy = new PassingTimeStrategy(); @Test void should_init_when_start_as_first_node_of_a_process_but_not_first_of_a_chain() { Invocation invocation = new Invocation(); invocation.setContext(ImmutableMap.of(CHAIN_START_TIME, "10")); strategy.start(invocation); assertThat(invocation.getContext(CHAIN_START_TIME)).isEqualTo("10"); assertThat(invocation.getLocalContext(CHAIN_START_TIME)).isEqualTo(10L); } @Test void should_do_nothing_when_start_not_as_first_node_of_a_process() { Invocation invocation = new Invocation(); invocation.setContext(ImmutableMap.of()); invocation.setLocalContext(ImmutableMap.of(CHAIN_START_TIME, 10L)); Throwable throwable = catchThrowable(() -> strategy.start(invocation)); assertThat(throwable).isNull(); } @Test void should_calc_elapsed_time_as_passing_time() { Invocation invocation = new Invocation(); invocation.addLocalContext(CHAIN_START_TIME, 10L); strategy.setClock(new MockClock(100L)); long elapsedNanoTime = strategy.calculateElapsedNanoTime(invocation); assertThat(elapsedNanoTime).isEqualTo(TimeUnit.MILLISECONDS.toNanos(90)); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.invocation.timeout; import static org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy.CHAIN_PROCESSING; import static org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy.CHAIN_START_TIME; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.test.scaffolding.time.MockTicker; import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableMap; class ProcessingTimeStrategyTest { ProcessingTimeStrategy strategy = new ProcessingTimeStrategy(); @Test void should_do_nothing_when_not_first_node_of_a_process() { Invocation invocation = new Invocation(); invocation.setLocalContext(ImmutableMap.of( CHAIN_START_TIME, 10L, CHAIN_PROCESSING, 0L )); Throwable throwable = catchThrowable(() -> strategy.start(invocation)); assertThat(throwable).isNull(); } @Test void should_calc_elapsed_time_as_processing_time() { strategy.setTicker(new MockTicker(50L)); Invocation invocation = new Invocation(); invocation.addLocalContext(CHAIN_START_TIME, 10L); invocation.addLocalContext(CHAIN_PROCESSING, 20L); long elapsedNanoTime = strategy.calculateElapsedNanoTime(invocation); assertThat(elapsedNanoTime).isEqualTo(60L); } @Test void should_update_processing_time_before_send() { strategy = new ProcessingTimeStrategy() { @Override public void checkTimeout(Invocation invocation) { } }; strategy.setTicker(new MockTicker(50L)); Invocation invocation = new Invocation(); invocation.addLocalContext(CHAIN_START_TIME, 10L); invocation.addLocalContext(CHAIN_PROCESSING, 20L); strategy.beforeSendRequest(invocation); assertThat(invocation.getContext(CHAIN_PROCESSING)).isEqualTo("60"); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/provider/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider; public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.consumer; import org.junit.jupiter.api.Test; public class TestInvokerUtils { @Test public void testRetryInvocation503() { } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; import java.util.Arrays; import java.util.concurrent.Executor; import org.apache.servicecomb.core.BootListener.BootEvent; import org.apache.servicecomb.core.BootListener.EventType; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestProducerBootListener { ProducerBootListener producerBootListener = new ProducerBootListener(); @Test public void onBootEvent_notClose() { BootEvent event = new BootEvent(); event.setEventType(EventType.BEFORE_CLOSE); // should not throw exception producerBootListener.onBootEvent(event); } @Test public void onBootEvent_close_unknown() { SCBEngine scbEngine = Mockito.mock(SCBEngine.class); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); OperationMeta op = Mockito.mock(OperationMeta.class); Executor executor = new UnCloseableExecutor(); Mockito.when(scbEngine.getProducerMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(microserviceMeta.getOperations()).thenReturn(Arrays.asList(op)); Mockito.when(op.getExecutor()).thenReturn(executor); try (LogCollector logCollector = new LogCollector()) { BootEvent event = new BootEvent(); event.setScbEngine(scbEngine); event.setEventType(EventType.AFTER_CLOSE); producerBootListener.onBootEvent(event); Assertions.assertEquals( "Executor org.apache.servicecomb.core.provider.producer.TestProducerBootListener$UnCloseableExecutor " + "do not support close or shutdown, it may block service shutdown.", logCollector.getLastEvents().getMessage().getFormattedMessage()); } } public static class UnCloseableExecutor implements Executor { @Override public void execute(Runnable command) { command.run(); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestProducerMeta { @Test public void test1() { Object instance = new Object(); ProducerMeta meta = new ProducerMeta("id", instance); Assertions.assertEquals("id", meta.getSchemaId()); Assertions.assertEquals(instance, meta.getInstance()); } @Test public void test2() { ProducerMeta meta = new ProducerMeta(); meta.setSchemaId("id1"); Assertions.assertEquals("id1", meta.getSchemaId()); meta.setInstance(1); Assertions.assertEquals(1, meta.getInstance()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.provider.producer; import org.junit.jupiter.api.Test; public class TestProducerProviderManager { @Test public void allowedNoProvider() { // must not throw exception new ProducerProviderManager(null).init(); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/registry/discovery/TestEndpointDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.registry.discovery; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestEndpointDiscoveryFilter { EndpointDiscoveryFilter filter = new EndpointDiscoveryFilter(); DiscoveryContext context = new DiscoveryContext(); Invocation invocation; @BeforeEach public void setup() { } @AfterEach public void teardown() { } @Test public void getOrder() { Assertions.assertEquals(Short.MAX_VALUE, filter.getOrder()); } @Test public void getTransportName() { invocation = Mockito.mock(Invocation.class); Mockito.when(invocation.getConfigTransportName()).thenReturn(CoreConst.RESTFUL); context.setInputParameters(invocation); Assertions.assertEquals(CoreConst.RESTFUL, filter.findTransportName(context, null)); } @Test public void createEndpointNullTransport() { TransportManager transportManager = Mockito.mock(TransportManager.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); Mockito.when(transportManager.findTransport(CoreConst.RESTFUL)).thenReturn(null); filter.setScbEngine(scbEngine); Assertions.assertNull(filter.createEndpoint(null, CoreConst.RESTFUL, "", null)); } @Test public void createEndpointNormal() { Transport transport = Mockito.mock(Transport.class); StatefulDiscoveryInstance instance = Mockito.mock(StatefulDiscoveryInstance.class); TransportManager transportManager = Mockito.mock(TransportManager.class); String endpoint = "rest://ip:port"; Object address = new Object(); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); Mockito.when(transportManager.findTransport(CoreConst.RESTFUL)).thenReturn(transport); Mockito.when(transport.parseAddress(endpoint)).thenReturn(address); filter.setScbEngine(scbEngine); Endpoint ep = (Endpoint) filter.createEndpoint(null, CoreConst.RESTFUL, endpoint, instance); Assertions.assertSame(transport, ep.getTransport()); Assertions.assertSame(address, ep.getAddress()); Assertions.assertSame(instance, ep.getMicroserviceInstance()); Assertions.assertEquals(endpoint, ep.getEndpoint()); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.tracing; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class BraveTraceIdGeneratorTest { @Test public void generateStringId() { TraceIdGenerator traceIdGenerator = new BraveTraceIdGenerator(); Assertions.assertNotEquals(traceIdGenerator.generate(), traceIdGenerator.generate()); String traceId = traceIdGenerator.generate(); try { Long.parseLong(traceId, 16); } catch (NumberFormatException e) { Assertions.fail("wrong traceId format: " + traceId); } } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.transport; import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.IpPort; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.impl.SysProps; public class TestAbstractTransport { Environment environment = Mockito.mock(Environment.class); static class MyAbstractTransport extends AbstractTransport { @Override public String getName() { return "my"; } @Override public boolean init() { return true; } } @BeforeEach public void setUp() { Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")).thenReturn(""); Mockito.when(environment.getProperty("servicecomb.my.publishPort", int.class, 0)).thenReturn(0); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } @Test public void testSetListenAddressWithoutSchemaChineseSpaceNewSC() throws UnsupportedEncodingException { MyAbstractTransport transport = new MyAbstractTransport(); transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "中 国")); Assertions.assertEquals("my://127.0.0.1:9090?country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()), transport.getEndpoint().getEndpoint()); } @Test public void testSetListenAddressWithoutSchemaNormalNotEncode() { MyAbstractTransport transport = new MyAbstractTransport(); transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "chinese")); Assertions.assertEquals("my://127.0.0.1:9090?country=chinese", transport.getEndpoint().getEndpoint()); } @Test public void testSetListenAddressWithoutSchemaAlreadyHaveQuery() { MyAbstractTransport transport = new MyAbstractTransport(); transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090?a=aValue", Collections.singletonMap("country", "chinese")); Assertions.assertEquals("my://127.0.0.1:9090?a=aValue&country=chinese", transport.getEndpoint().getEndpoint()); } @Test public void testMyAbstractTransport() { MyAbstractTransport transport = new MyAbstractTransport(); transport.setEnvironment(environment); transport.setListenAddressWithoutSchema("127.0.0.1:9090"); Assertions.assertEquals("my", transport.getName()); Assertions.assertEquals("my://127.0.0.1:9090", transport.getEndpoint().getEndpoint()); Assertions.assertEquals("127.0.0.1", ((IpPort) transport.parseAddress("my://127.0.0.1:9090")).getHostOrIp()); transport.setListenAddressWithoutSchema("0.0.0.0:9090"); Assertions.assertNotEquals("my://127.0.0.1:9090", transport.getEndpoint().getEndpoint()); transport.setListenAddressWithoutSchema(null); Assertions.assertNull(transport.getEndpoint().getEndpoint()); Assertions.assertNull(transport.parseAddress(null)); } @Test public void testMyAbstractTransportException() { MyAbstractTransport transport = new MyAbstractTransport(); transport.setEnvironment(environment); Assertions.assertThrows(IllegalArgumentException.class, () -> transport.setListenAddressWithoutSchema(":127.0.0.1:9090")); } } ================================================ FILE: core/src/test/java/org/apache/servicecomb/core/transport/TestTransportManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core.transport; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestTransportManager { @Test public void testTransportManagerInitFail() throws Exception { SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Transport transport = Mockito.mock(Transport.class); Mockito.when(transport.getName()).thenReturn("test"); Mockito.when(transport.init()).thenReturn(false); Mockito.when(transport.canInit()).thenReturn(true); List transports = Arrays.asList(transport); TransportManager manager = new TransportManager(); manager.addTransportsBeforeInit(transports); manager.init(scbEngine); Assertions.assertEquals(manager.findTransport("test"), transport); } @Test public void testTransportManagerInitSuccess() throws Exception { SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Transport transport = Mockito.mock(Transport.class); Endpoint endpoint = Mockito.mock(Endpoint.class); Mockito.when(transport.getName()).thenReturn("test"); Mockito.when(transport.init()).thenReturn(true); Mockito.when(transport.canInit()).thenReturn(true); Mockito.when(transport.getPublishEndpoint()).thenReturn(endpoint); List transports = Arrays.asList(transport); TransportManager manager = new TransportManager(); manager.addTransportsBeforeInit(transports); manager.init(scbEngine); Assertions.assertEquals(manager.findTransport("test"), transport); } @Test public void testGroupByName() { Transport t1 = Mockito.mock(Transport.class); Mockito.when(t1.getName()).thenReturn("t1"); Transport t21 = Mockito.mock(Transport.class); Mockito.when(t21.getName()).thenReturn("t2"); Transport t22 = Mockito.mock(Transport.class); Mockito.when(t22.getName()).thenReturn("t2"); TransportManager manager = new TransportManager(); manager.addTransportsBeforeInit(Arrays.asList(t1, t21, t22)); Map> groups = manager.groupByName(); Assertions.assertEquals(2, groups.size()); Assertions.assertEquals(1, groups.get("t1").size()); Assertions.assertEquals(t1, groups.get("t1").get(0)); Assertions.assertEquals(2, groups.get("t2").size()); Assertions.assertEquals(t21, groups.get("t2").get(0)); Assertions.assertEquals(t22, groups.get("t2").get(1)); } @Test public void testCheckTransportGroupInvalid() { Transport t1 = Mockito.mock(Transport.class); Mockito.when(t1.getOrder()).thenReturn(1); Transport t2 = Mockito.mock(Transport.class); Mockito.when(t2.getOrder()).thenReturn(1); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); try { manager.checkTransportGroup(group); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertTrue(e.getMessage().contains("have the same order")); } } @Test public void testCheckTransportGroupValid() { Transport t1 = Mockito.mock(Transport.class); Mockito.when(t1.getOrder()).thenReturn(1); Transport t2 = Mockito.mock(Transport.class); Mockito.when(t2.getOrder()).thenReturn(2); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); try { manager.checkTransportGroup(group); } catch (ServiceCombException e) { Assertions.fail("must not throw exception: " + e.getMessage()); } } @Test public void testChooseOneTransportFirst() { Transport t1 = Mockito.mock(Transport.class); Mockito.when(t1.getOrder()).thenReturn(1); Mockito.when(t1.canInit()).thenReturn(true); Transport t2 = Mockito.mock(Transport.class); Mockito.when(t2.getOrder()).thenReturn(2); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); Assertions.assertEquals(t1, manager.chooseOneTransport(group)); } @Test public void testChooseOneTransportSecond() { Transport t1 = Mockito.mock(Transport.class); Mockito.when(t1.getOrder()).thenReturn(Integer.MAX_VALUE); Mockito.when(t1.canInit()).thenReturn(true); Transport t2 = Mockito.mock(Transport.class); Mockito.when(t2.getOrder()).thenReturn(-1000); Mockito.when(t2.canInit()).thenReturn(false); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); Assertions.assertEquals(t1, manager.chooseOneTransport(group)); } @Test public void testChooseOneTransportNone() { Transport t1 = Mockito.mock(Transport.class); Mockito.when(t1.getName()).thenReturn("t"); Mockito.when(t1.getOrder()).thenReturn(1); Mockito.when(t1.canInit()).thenReturn(false); Transport t2 = Mockito.mock(Transport.class); Mockito.when(t2.getOrder()).thenReturn(2); Mockito.when(t2.canInit()).thenReturn(false); TransportManager manager = new TransportManager(); List group = Arrays.asList(t1, t2); try { manager.chooseOneTransport(group); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals("all transport named t refused to init.", e.getMessage()); } } } ================================================ FILE: core/src/test/resources/META-INF/spring/cse.bean.xml ================================================ ================================================ FILE: core/src/test/resources/log4j2.xml ================================================ ================================================ FILE: core/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- APPLICATION_ID: app service_description: name: perfClient version: 0.0.1 servicecomb: service: registry: address: http://127.0.0.1:30100 handler: chain: Consumer: default: simpleLB zq: - tlist: a - tlist: b - tlist: 1 ================================================ FILE: core/src/test/resources/test/test/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- service_description: name: default version: 0.0.1 propertyExtendedClass: org.apache.servicecomb.serviceregistry.MicroServicePropertyExtendedStub servicecomb: service: registry: address: http://127.0.0.1:30100 client: httpVersion: HTTP_1_1 workerPoolSize: 1 timeout: connection: 30000 idle: 30000 instance: watch: true preferIpAddress: false healthCheck: interval: 1 times: 5 #ssl.keystore.path= #ssl.keystore.pass= #ssl.truststore.path= #ssl.truststore.pass= ================================================ FILE: core/src/test/resources/test/test/schema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema" x-java-interface: "gen.cse.ms.ut.SchemaIntf" basePath: "/Schema" consumes: - "application/json" produces: - "application/json" paths: /testint: post: operationId: "testint" parameters: - in: "body" name: "value" required: false schema: $ref: "#/definitions/testintBody" responses: "200": description: "response of 200" definitions: testintBody: type: "object" properties: value: type: "integer" format: "int32" ================================================ FILE: coverage-reports/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 coverage-reports jar org.apache.servicecomb config-center-client org.apache.servicecomb config-clients-common org.apache.servicecomb config-kie-client org.apache.servicecomb dashboard-client org.apache.servicecomb http-client-common org.apache.servicecomb service-center-client org.apache.servicecomb common-access-log org.apache.servicecomb common-protobuf org.apache.servicecomb common-rest org.apache.servicecomb java-chassis-core org.apache.servicecomb config-apollo org.apache.servicecomb config-cc org.apache.servicecomb config-kie org.apache.servicecomb config-nacos org.apache.servicecomb edge-core org.apache.servicecomb foundation-common org.apache.servicecomb foundation-config org.apache.servicecomb foundation-metrics org.apache.servicecomb foundation-protobuf org.apache.servicecomb foundation-registry org.apache.servicecomb foundation-spi org.apache.servicecomb foundation-ssl org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb foundation-vertx org.apache.servicecomb servicecomb-governance org.apache.servicecomb handler-fault-injection org.apache.servicecomb handler-flowcontrol-qps org.apache.servicecomb handler-loadbalance org.apache.servicecomb handler-publickey-auth org.apache.servicecomb handler-router org.apache.servicecomb handler-tracing-zipkin org.apache.servicecomb metrics-core org.apache.servicecomb metrics-prometheus org.apache.servicecomb provider-jaxrs org.apache.servicecomb provider-pojo org.apache.servicecomb provider-rest-common org.apache.servicecomb provider-springmvc org.apache.servicecomb registry-lightweight org.apache.servicecomb registry-local org.apache.servicecomb registry-service-center org.apache.servicecomb registry-zero-config org.apache.servicecomb solution-basic org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb servicestage org.apache.servicecomb dashboard org.apache.servicecomb darklaunch org.apache.servicecomb swagger-generator-core org.apache.servicecomb swagger-generator-jaxrs org.apache.servicecomb swagger-generator-spring-data org.apache.servicecomb swagger-generator-springmvc org.apache.servicecomb swagger-invocation-core org.apache.servicecomb swagger-invocation-jaxrs org.apache.servicecomb swagger-invocation-springmvc org.apache.servicecomb swagger-invocation-validator org.apache.servicecomb tracing-common org.apache.servicecomb tracing-zipkin org.apache.servicecomb transport-common org.apache.servicecomb transport-highway org.apache.servicecomb transport-rest-client org.apache.servicecomb transport-rest-servlet org.apache.servicecomb transport-rest-vertx org.apache.servicecomb.demo crossapp-client ${project.version} test org.apache.servicecomb.demo crossapp-server ${project.version} test org.apache.servicecomb.demo authentication ${project.version} test org.apache.servicecomb.demo business-1-0-0 ${project.version} test org.apache.servicecomb.demo business-1-1-0 ${project.version} test org.apache.servicecomb.demo business-2-0-0 ${project.version} test org.apache.servicecomb.demo consumer ${project.version} test org.apache.servicecomb.demo edge-service ${project.version} test org.apache.servicecomb.demo model ${project.version} test org.apache.servicecomb.demo jaxrs-client ${project.version} test org.apache.servicecomb.demo jaxrs-server ${project.version} test org.apache.servicecomb.demo demo-local-registry-client ${project.version} test org.apache.servicecomb.demo demo-local-registry-server ${project.version} test org.apache.servicecomb.demo demo-multi-registries-client ${project.version} test org.apache.servicecomb.demo demo-multi-registries-server ${project.version} test org.apache.servicecomb.demo pojo-client ${project.version} test org.apache.servicecomb.demo pojo-server ${project.version} test org.apache.servicecomb.demo demo-register-url-prefix-client ${project.version} test org.apache.servicecomb.demo demo-register-url-prefix-server ${project.version} test org.apache.servicecomb.demo demo-schema ${project.version} test org.apache.servicecomb.demo demo-spring-boot-pojo-client ${project.version} test org.apache.servicecomb.demo demo-spring-boot-pojo-server ${project.version} test org.apache.servicecomb.demo demo-spring-boot-springmvc-client ${project.version} test org.apache.servicecomb.demo demo-spring-boot-springmvc-server ${project.version} test org.apache.servicecomb.demo springmvc-client ${project.version} test org.apache.servicecomb.demo springmvc-server ${project.version} test org.apache.servicecomb.demo demo-zeroconfig-registry-client ${project.version} test org.apache.servicecomb.demo demo-zeroconfig-registry-server ${project.version} test org.apache.servicecomb.demo demo-zeroconfig-registry-tests ${project.version} test jacoco false org.jacoco jacoco-maven-plugin verify report-aggregate ================================================ FILE: demo/README.md ================================================ This module contains integrated test cases. Running these test cases, should include profile ```shell script -Pdocker ``` [java-chassis-samples][java-chassis-samples] project provides a lot of samples. [java-chassis-samples]: https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples ================================================ FILE: demo/assembly/assembly.xml ================================================ bin dir false /maven ${project.build.directory} **/*.jar /maven ${project.build.directory}/classes certificates/* ================================================ FILE: demo/demo-consul/README.md ================================================ # Notice This integration tests is designed for Consul registry and configuration. And extra test cases include: * Test cases related to SpringMVC annotations that demo-springmvc can not cover. ================================================ FILE: demo/demo-consul/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-consul 3.4.0-SNAPSHOT consul-consumer Java Chassis::Demo::Consul::CONSUMER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-consul org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.ServerWebSocket; import io.vertx.core.http.WebSocket; @RestSchema(schemaId = "ClientWebsocketController") @RequestMapping(path = "/ws") public class ClientWebsocketController { interface ProviderService { WebSocket websocket(); } @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") private ProviderService providerService; @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) public void websocket(ServerWebSocket serverWebsocket) { WebSocket providerWebSocket = providerService.websocket(); providerWebSocket.closeHandler(v -> serverWebsocket.close()); providerWebSocket.textMessageHandler(m -> { serverWebsocket.writeTextMessage(m); }); serverWebsocket.textMessageHandler(m -> { providerWebSocket.writeTextMessage(m); }); } } ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsulConsumerApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsulConsumerApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @RestSchema(schemaId = "ConsumerController") @RequestMapping(path = "/") public class ConsumerController { @RpcReference(schemaId = "ProviderController", microserviceName = "provider") private ProviderService providerService; // consumer service which delegate the implementation to provider service. @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } @GetMapping("/getConfig") public String getConfig(@RequestParam("key") String key) { return providerService.getConfig(key); } @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public User testContentType(@RequestBody User user) { return providerService.testContentType(user); } } ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") private IHeaderParamWithListSchemaSpringMvc provider; @Override public String headerListDefault(List headerList) { return provider.headerListDefault(headerList); } @Override public String headerListCSV(List headerList) { return provider.headerListCSV(headerList); } @Override public String headerListMULTI(List headerList) { return provider.headerListMULTI(headerList); } @Override public String headerListSSV(List headerList) { return provider.headerListSSV(headerList); } @Override public String headerListPIPES(List headerList) { return provider.headerListPIPES(headerList); } } ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") public class ConsumerReactiveStreamController { interface ProviderReactiveStreamController { Publisher sseString(); Publisher sseModel(); } @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") ProviderReactiveStreamController controller; public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") @Transport(name = CoreConst.RESTFUL) public Publisher sseString() { return controller.sseString(); } @GetMapping("/sseModel") @Transport(name = CoreConst.RESTFUL) public Publisher sseModel() { return controller.sseModel(); } } ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface ProviderService { String sayHello(String name); String getConfig(String key); User testContentType(User user); } ================================================ FILE: demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-consul/consumer/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-consul version: 0.0.1 name: consumer properties: group: green registry: consul: enabled: true host: 127.0.0.1 scheme: http discovery: enabled: true acl-token: '' watch-seconds: 8 rest: address: 0.0.0.0:9092?websocketEnabled=true server: websocket-prefix: /ws ================================================ FILE: demo/demo-consul/consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-consul/gateway/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-consul 3.4.0-SNAPSHOT consul-gateway Java Chassis::Demo::Consul::GATEWAY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb edge-core org.apache.servicecomb registry-consul org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-consul/gateway/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-consul version: 0.0.1 name: gateway registry: consul: enabled: true host: 127.0.0.1 scheme: http discovery: enabled: true acl-token: '' watch-seconds: 8 rest: address: 0.0.0.0:9090?websocketEnabled=true server: websocket-prefix: /ws http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: consumer: prefixSegmentCount: 0 path: "/.*" microserviceName: consumer versionRule: 0.0.0+ websocket: mappings: consumer: prefixSegmentCount: 0 path: "/ws/.*" microserviceName: consumer versionRule: 0.0.0+ ================================================ FILE: demo/demo-consul/gateway/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-consul/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-consul Java Chassis::Demo::Consul pom org.apache.servicecomb.demo demo-schema org.apache.servicecomb solution-basic org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-core org.apache.logging.log4j log4j-api provider consumer gateway test-client ================================================ FILE: demo/demo-consul/provider/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-consul 3.4.0-SNAPSHOT consul-provider Java Chassis::Demo::Consul::PROVIDER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-consul org.apache.servicecomb config-consul io.reactivex.rxjava3 rxjava org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsulProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsulProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { @Override public String headerListDefault(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListCSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListMULTI(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListSSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListPIPES(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } } ================================================ FILE: demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController implements InitializingBean { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { // return "Hello " + environment.getProperty("servicecomb.rest.address"); return "Hello " + name; } @GetMapping("/getConfig") public String getConfig(@RequestParam("key") String key) { return environment.getProperty(key); } @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public User testContentType(@RequestBody User user) { return user; } @Override public void afterPropertiesSet() throws Exception { } } ================================================ FILE: demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.reactivex.rxjava3.core.Flowable; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") @Transport(name = CoreConst.RESTFUL) public class ReactiveStreamController { public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") public Publisher sseString() { return Flowable.fromArray("a", "b", "c"); } @GetMapping("/sseModel") public Publisher sseModel() { return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) .map(item -> new Model("jack", item.intValue())); } } ================================================ FILE: demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.ServerWebSocket; @RestSchema(schemaId = "WebsocketController") @RequestMapping(path = "/ws") public class WebsocketController { @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) public void websocket(ServerWebSocket serverWebsocket) { AtomicInteger receiveCount = new AtomicInteger(0); CountDownLatch startSend = new CountDownLatch(1); serverWebsocket.textMessageHandler(s -> { if ("start".equals(s)) { startSend.countDown(); serverWebsocket.writeTextMessage("started"); return; } receiveCount.getAndIncrement(); }); serverWebsocket.closeHandler((v) -> System.out.println("closed")); new Thread(() -> { try { startSend.await(30, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } serverWebsocket.writeTextMessage("hello"); for (int i = 0; i < 5; i++) { serverWebsocket.writeTextMessage("hello " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } serverWebsocket.writeTextMessage("total " + receiveCount.get()); serverWebsocket.close(); }).start(); } } ================================================ FILE: demo/demo-consul/provider/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations servicecomb: service: application: demo-consul version: 0.0.1 name: provider properties: group: green registry: consul: enabled: true host: 127.0.0.1 scheme: http discovery: enabled: true acl-token: '' watch-seconds: 8 config: consul: enabled: true host: 127.0.0.1 scheme: http acl-token: '' watch-seconds: 8 rest: address: 0.0.0.0:9094?websocketEnabled=true server: websocket-prefix: /ws cors: enabled: true origin: "*" allowCredentials: false allowedMethod: "*" maxAge: 3600 key1: 1 key2: 3 key3: 5 test1: env test2: applition test3: service test4: version test5: tag ================================================ FILE: demo/demo-consul/provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-consul/test-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-consul 3.4.0-SNAPSHOT consul-test-client Java Chassis::Demo::Consul::TEST-CLIENT jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-local org.kiwiproject consul-client com.google.code.gson gson docker io.fabric8 docker-maven-plugin hashicorp/consul:1.21 consul alias consulServer 8500 8500:8500 yes consul-provider:${project.version} consul-provider alias -Dservicecomb.registry.consul.enabled=true -Dservicecomb.registry.consul.scheme=http -Dservicecomb.registry.consul.host=consul -Dservicecomb.registry.consul.port=8500 -Dservicecomb.config.consul.enabled=true -Dservicecomb.config.consul.scheme=http -Dservicecomb.config.consul.host=consul -Dservicecomb.config.consul.port=8500 /maven/maven/consul-provider-${project.version}.jar consul:consul ServiceComb is ready 9094 9094:9094 consul-consumer:${project.version} consul-consumer alias -Dservicecomb.registry.consul.enabled=true -Dservicecomb.registry.consul.scheme=http -Dservicecomb.registry.consul.host=consul -Dservicecomb.registry.consul.port=8500 /maven/maven/consul-consumer-${project.version}.jar consul:consul ServiceComb is ready 9092 9092:9092 consul-gateway:${project.version} consul-gateway alias -Dservicecomb.registry.consul.enabled=true -Dservicecomb.registry.consul.scheme=http -Dservicecomb.registry.consul.host=consul -Dservicecomb.registry.consul.port=8500 /maven/maven/consul-gateway-${project.version}.jar consul:consul ServiceComb is ready 9090 9090:9090 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface Config { String GATEWAY_URL = "http://localhost:9090"; } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import com.google.common.net.HostAndPort; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.ConditionWaiter.SleepUtil; import org.kiwiproject.consul.Consul; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; import java.util.concurrent.TimeUnit; @Component public class ConsulConfigIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); private static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigIT.class); @Override public void testRestTransport() throws Exception { testEnvironment(); testApplication(); testService(); testVersion(); testTag(); testOverride(); } private void testOverride() { putValue("/servicecomb/config/environment/production/application2.properties", "testValue=t1"); putValue("/servicecomb/config/application/production/demo-consul/application2.properties", "testValue=t2"); testGetConfig("testValue", "t2"); putValue("/servicecomb/config/service/production/demo-consul/provider/application2.properties", "testValue=t3"); testGetConfig("testValue", "t3"); putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application2.properties", "testValue=t4"); testGetConfig("testValue", "t4"); putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application2.properties", "testValue=t5"); testGetConfig("testValue", "t5"); } private void testEnvironment() { putValue("/servicecomb/config/environment/production/application.properties", "test1=env"); putValue("/servicecomb/config/environment/production/application.properties", "test1=env1"); testGetConfig("test1", "env1"); } private void testApplication() { putValue("/servicecomb/config/application/production/demo-consul/application.properties", "test2=applition"); putValue("/servicecomb/config/application/production/demo-consul/application.properties", "test2=applition2"); testGetConfig("test2", "applition2"); } private void testService() { putValue("/servicecomb/config/service/production/demo-consul/provider/application.properties", "test3=service"); putValue("/servicecomb/config/service/production/demo-consul/provider/application.properties", "test3=service3"); testGetConfig("test3", "service3"); } private void testVersion() { putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application.properties", "test3=version"); putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application.properties", "test4=version4"); testGetConfig("test4", "version4"); } private void testTag() { putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application.properties", "test5=tag"); putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application.properties", "test5=tag5"); testGetConfig("test5", "tag5"); } public void putValue(String key, String value) { try { Consul.Builder builder = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", 8500)); Consul consulClient = builder.build(); consulClient.keyValueClient().putValue(key, value); LOGGER.info("Value set successfully:{}", value); } catch (Exception e) { e.printStackTrace(); } } private void testGetConfig(String key, String expectValue) { String result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); for (int i = 0; i < 4; i++) { if (StringUtils.equals(expectValue, result)) { TestMgr.check(expectValue, result); break; } SleepUtil.sleep(500, TimeUnit.MILLISECONDS); } } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HeaderParamWithListSchemaIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHeaderListDefault(); testHeaderListMulti(); testHeaderListCSV(); testHeaderListSSV(); testHeaderListPipes(); } // default to multi private void testHeaderListDefault() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListPipes() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a|b|c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListSSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a b c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListCSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a,b,c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); headers.add("headerList", "a, b, c"); entity = new HttpEntity<>(headers); result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListMulti() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHelloWorld(); testGetConfig(); } private void testGetConfig() { String result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); TestMgr.check("1", result); result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); TestMgr.check("3", result); result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); TestMgr.check("5", result); } private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); TestMgr.check("Hello World", result); // test trace id added MultiValueMap headers = new HttpHeaders(); headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); HttpEntity entity = new HttpEntity(headers); ResponseEntity response = template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); TestMgr.check("Hello World", response.getBody()); } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class ProviderIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); private static final Logger LOGGER = LoggerFactory.getLogger(ProviderIT.class); @Override public void testRestTransport() throws Exception { User user = getUser("Application/json"); TestMgr.check(1L, user.getId()); TestMgr.check("czd", user.getName()); User user2 = getUser("application/json"); TestMgr.check(1L, user2.getId()); TestMgr.check("czd", user2.getName()); User user3 = getUser("APPLICATION/JSON"); TestMgr.check(1L, user3.getId()); TestMgr.check("czd", user3.getName()); } private User getUser(String contentType) throws IOException { HttpHeaders headers = new HttpHeaders(); headers.set("Content-Type", contentType); String requestBody = """ { "id": 1, "name": "czd" } """; HttpEntity entity = new HttpEntity<>(requestBody, headers); String url = Config.GATEWAY_URL + "/testContentType"; ResponseEntity response = template.exchange( url, HttpMethod.POST, entity, String.class); User user = JsonUtils.readValue(response.getBody().getBytes(StandardCharsets.UTF_8), User.class); return user; } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class ReactiveStreamIT implements CategorizedTestCase { @Autowired @Qualifier("reactiveStreamProvider") ReactiveStreamClient reactiveStreamProvider; @Autowired @Qualifier("reactiveStreamGateway") ReactiveStreamClient reactiveStreamGateway; @Override public void testRestTransport() throws Exception { testSseString(reactiveStreamProvider); testSseModel(reactiveStreamProvider); testSseString(reactiveStreamGateway); testSseModel(reactiveStreamGateway); } private void testSseModel(ReactiveStreamClient client) throws Exception { Publisher result = client.sseModel(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(Model s) { buffer.append(s.getName()).append(s.getAge()); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); } private void testSseString(ReactiveStreamClient client) throws Exception { Publisher result = client.sseString(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(String s) { buffer.append(s); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("abc", buffer.toString()); } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.apache.servicecomb.provider.pojo.Invoker; import org.reactivestreams.Publisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.WebSocket; @Configuration public class ThirdSvcConfiguration { @RequestMapping(path = "/ws") public interface WebsocketClient { @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) WebSocket websocket(); } @RequestMapping(path = "/") public interface ReactiveStreamClient { class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") Publisher sseString(); @GetMapping("/sseModel") Publisher sseModel(); } @Bean public RegistryBean providerServiceBean() { return new RegistryBean() .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) .setAppId("demo-consul") .setServiceName("provider") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of( new Instance().setEndpoints(List.of("rest://localhost:9094"))))); } @Bean public RegistryBean gatewayServiceBean() { return new RegistryBean() .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) .addSchemaInterface("WebsocketController", WebsocketClient.class) .setAppId("demo-consul") .setServiceName("gateway") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of( new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); } @Bean("reactiveStreamProvider") public ReactiveStreamClient reactiveStreamProvider() { return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); } @Bean("reactiveStreamGateway") public ReactiveStreamClient reactiveStreamGateway() { return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); } @Bean public WebsocketClient gatewayWebsocketClient() { return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import io.vertx.core.http.WebSocket; @Component public class WebsocketIT implements CategorizedTestCase { @Autowired private WebsocketClient websocketClient; @Override public void testRestTransport() throws Exception { StringBuffer sb = new StringBuffer(); AtomicBoolean closed = new AtomicBoolean(false); CountDownLatch latchStarted = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1); WebSocket webSocket = websocketClient.websocket(); webSocket.textMessageHandler(s -> { if ("started".equals(s)) { latchStarted.countDown(); return; } sb.append(s); sb.append(" "); webSocket.writeTextMessage(s); }); webSocket.closeHandler(v -> { closed.set(true); latch.countDown(); }); webSocket.writeTextMessage("start"); int i = 0; for (; i < 10; i++) { if (!latchStarted.await(3, TimeUnit.SECONDS)) { webSocket.writeTextMessage("start"); continue; } break; } TestMgr.check(i < 10, true); latch.await(30, TimeUnit.SECONDS); TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); TestMgr.check(closed.get(), true); } } ================================================ FILE: demo/demo-consul/test-client/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-consul name: test-client version: 0.0.1 rest: address: 0.0.0.0:9097 # should be same with server.port to use web container config: consul: enabled: true host: 127.0.0.1 scheme: http acl-token: '' instance-tag: tag1 test1: env test2: applition test3: service test4: version test5: tag ================================================ FILE: demo/demo-consul/test-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = TestClientApplication.class) public class ConsulIT { private static final Logger LOGGER = LoggerFactory.getLogger(ConsulIT.class); @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() { try { TestClientApplication.run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-crossapp/crossapp-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-crossapp 3.4.0-SNAPSHOT crossapp-client Java Chassis::Demo::CrossApp::Client org.apache.servicecomb.demo.crossapp.CrossappClient docker crossapp-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.crossapp; import java.util.Collections; import java.util.TreeSet; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class CrossappClient { @RpcReference(microserviceName = "appServer:appService", schemaId = "helloworld") private static HelloWorld helloWorld; public static void main(String[] args) throws Exception { new SpringApplicationBuilder(CrossappClient.class).web(WebApplicationType.NONE).run(args); run(); } public static void run() { System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); String result = InvokerUtils.syncInvoke("appServer:appService", "helloworld", "sayHello", null, String.class); TestMgr.check("hello world", result); RestOperations restTemplate = RestTemplateBuilder.create(); result = restTemplate.getForObject("cse://appServer:appService/helloworld/hello", String.class); TestMgr.check("hello world", result); result = restTemplate.getForObject("servicecomb://appServer:appService/helloworld/hello", String.class); TestMgr.check("hello world", result); result = helloWorld.sayHello(); TestMgr.check("hello world", result); testCorsHandlerOptions(); testCorsHandlerGet(); TestMgr.summary(); System.setProperty("sun.net.http.allowRestrictedHeaders", "false"); } private static void testCorsHandlerOptions() { // first domain RestOperations springRestTemplate = new RestTemplate(); MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); requestHeaders.put("Origin", Collections.singletonList("http://test.domain:8080")); requestHeaders.put("Access-Control-Request-Method", Collections.singletonList("PUT")); HttpEntity requestEntity = new HttpEntity<>(requestHeaders); ResponseEntity responseEntity = springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.OPTIONS, requestEntity, String.class); TestMgr.check("204", responseEntity.getStatusCode().value()); TreeSet sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Methods")); TestMgr.check("[DELETE,POST,GET,PUT]", sortedSet); sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Headers")); TestMgr.check("[abc,def]", sortedSet); TestMgr.check("http://test.domain:8080", responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); // second domain requestHeaders = new LinkedMultiValueMap<>(); requestHeaders.put("Origin", Collections.singletonList("http://test.domain:9090")); requestHeaders.put("Access-Control-Request-Method", Collections.singletonList("PUT")); requestEntity = new HttpEntity<>(requestHeaders); responseEntity = springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.OPTIONS, requestEntity, String.class); TestMgr.check("204", responseEntity.getStatusCode().value()); sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Methods")); TestMgr.check("[DELETE,POST,GET,PUT]", sortedSet); sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Headers")); TestMgr.check("[abc,def]", sortedSet); TestMgr.check("http://test.domain:9090", responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); } private static void testCorsHandlerGet() { // allowed origin RestOperations springRestTemplate = new RestTemplate(); MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); requestHeaders.put("Origin", Collections.singletonList("http://test.domain:8080")); HttpEntity requestEntity = new HttpEntity<>(requestHeaders); ResponseEntity responseEntity = springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, String.class); TestMgr.check("200", responseEntity.getStatusCode().value()); TestMgr.check("hello world", responseEntity.getBody()); // allowed origin requestHeaders = new LinkedMultiValueMap<>(); requestHeaders.put("Origin", Collections.singletonList("http://test.domain:9090")); requestEntity = new HttpEntity<>(requestHeaders); responseEntity = springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, String.class); TestMgr.check("200", responseEntity.getStatusCode().value()); TestMgr.check("hello world", responseEntity.getBody()); // not allowed origin try { requestHeaders = new LinkedMultiValueMap<>(); requestHeaders.put("Origin", Collections.singletonList("http://test.domain:7070")); requestEntity = new HttpEntity<>(requestHeaders); springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, String.class); TestMgr.fail("must throw"); } catch (HttpServerErrorException e) { TestMgr.check(500, e.getStatusCode().value()); TestMgr.check(true, e.getMessage().contains("500 CORS Rejected")); } } } ================================================ FILE: demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorld.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.crossapp; public interface HelloWorld { String sayHello(); } ================================================ FILE: demo/demo-crossapp/crossapp-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: appClient name: appClientService version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 handler: chain: Consumer: default: loadbalance loadbalance: appServer:appService: transactionControl: policy: org.apache.servicecomb.loadbalance.filter.SimpleTransactionControlFilter options: tag0: value0 ================================================ FILE: demo/demo-crossapp/crossapp-client/src/test/java/org/apache/servicecomb/demo/crossapp/CrossAppIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.crossapp; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = CrossappClient.class) public class CrossAppIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { CrossappClient.run(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-crossapp/crossapp-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-crossapp 3.4.0-SNAPSHOT crossapp-server Java Chassis::Demo::CrossApp::Server org.apache.servicecomb.demo.crossapp.CrossappServer org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.crossapp; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class CrossappServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(CrossappServer.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorldImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.crossapp; import org.apache.servicecomb.provider.rest.common.RestSchema; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @RestSchema(schemaId = "helloworld") @Path("helloworld") public class HelloWorldImpl { @GET @Path("hello") public String sayHello() { return "hello world"; } } ================================================ FILE: demo/demo-crossapp/crossapp-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: appServer name: appService version: 0.0.1 properties: allowCrossApp: true tag0: value0 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 cors: enabled: true origin: "http://test.domain:8080,http://test.domain:9090" allowedHeader: abc,def allowedMethod: GET,PUT,POST,DELETE exposedHeader: abc,def maxAge: 1 ================================================ FILE: demo/demo-crossapp/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-crossapp Java Chassis::Demo::CrossApp pom crossapp-server crossapp-client org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-service-center org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-cse-v1/README.md ================================================ # 使用微服务引擎专业版的测试用例 * 首先登陆华为云华南区,获取 AK/SK。 测试机器设置环境变量: CREDENTIALS_AK、CREDENTIALS_SK。 * 在配置中心增加如下配置: * consumer.yaml ```yaml cse: v1: test: foo: foo dynamicString: a,b dynamicArray: - m - n ``` * 依次启动 provider、provider-canary、consumer、gateway * 在配置中心增加如下配置: * cse.v1.test.bar: bar * 执行 tests-client 里面的集成测试用例 ================================================ FILE: demo/demo-cse-v1/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v1 3.4.0-SNAPSHOT cse-v1-consumer Java Chassis::Demo::CSE-V1::CONSUMER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ConsumerConfigController") @RequestMapping(path = "/") public class ConsumerConfigController { private Environment environment; private ConsumerConfigurationProperties consumerConfigurationProperties; @Autowired public ConsumerConfigController(Environment environment, ConsumerConfigurationProperties consumerConfigurationProperties) { this.environment = environment; this.consumerConfigurationProperties = consumerConfigurationProperties; } @GetMapping("/config") public String config(@RequestParam("key") String key) { return environment.getProperty(key); } @GetMapping("/foo") public String foo() { return consumerConfigurationProperties.getFoo(); } @GetMapping("/bar") public String bar() { return consumerConfigurationProperties.getBar(); } @GetMapping("/dynamicString") public String dynamicString(@RequestParam("key") String key) { return environment.getProperty(key); } @GetMapping("/dynamicArray") @SuppressWarnings("unchecked") public List dynamicArray() { return consumerConfigurationProperties.getDynamicArray(); } } ================================================ FILE: demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @ConfigurationProperties("cse.v1.test") @Component public class ConsumerConfigurationProperties { private String foo; private String bar; private List dynamicArray; public String getFoo() { return foo; } public void setFoo(String foo) { this.foo = foo; } public String getBar() { return bar; } public void setBar(String bar) { this.bar = bar; } public List getDynamicArray() { return dynamicArray; } public void setDynamicArray(List dynamicArray) { this.dynamicArray = dynamicArray; } } ================================================ FILE: demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ConsumerController") @RequestMapping(path = "/") public class ConsumerController { @RpcReference(schemaId = "ProviderController", microserviceName = "provider") private ProviderService providerService; // consumer service which delegate the implementation to provider service. @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } @GetMapping("/sayHelloCanary") public String sayHelloCanary(@RequestParam("name") String name) { return providerService.sayHelloCanary(name); } } ================================================ FILE: demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface ProviderService { String sayHello(String name); String sayHelloCanary(String name); } ================================================ FILE: demo/demo-cse-v1/consumer/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v1 name: consumer version: 0.0.1 registry: sc: address: https://cse.cn-south-1.myhuaweicloud.com watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com fileSource: consumer.yaml rest: address: 0.0.0.0:9092 # should be same with server.port to use web container routeRule: provider: | - precedence: 1 match: headers: canary: exact: new route: - weight: 20 tags: version: 0.0.1 - weight: 80 tags: version: 0.0.2 - precedence: 2 match: headers: canary: exact: old route: - weight: 100 tags: version: 0.0.1 - precedence: 3 match: headers: canary: exact: fallback route: - weight: 100 tags: version: 0.0.3 fallback: - weight: 20 tags: version: 0.0.1 - weight: 80 tags: version: 0.0.2 - precedence: 4 emptyProtection: false match: headers: canary: exact: emptyProtectionClose100 route: - weight: 100 tags: version: 0.0.3 - precedence: 5 emptyProtection: false match: headers: canary: exact: emptyProtectionCloseLess100 route: - weight: 50 tags: version: 0.0.3 - precedence: 6 emptyProtection: false match: headers: canary: exact: emptyProtectionCloseFallback route: - weight: 100 tags: version: 0.0.3 fallback: - weight: 100 tags: version: 0.0.1 - precedence: 7 emptyProtection: false match: headers: canary: exact: emptyProtectionClose100-2 route: - weight: 50 tags: version: 0.0.1 - weight: 50 tags: version: 0.0.3 router: type: router header: canary # Configure AK/SK credentials if needed. Default not enabled. credentials: akskEnabled: true accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 ================================================ FILE: demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml ================================================ ./file/log/ ================================================ FILE: demo/demo-cse-v1/gateway/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v1 3.4.0-SNAPSHOT cse-v1-gateway Java Chassis::Demo::CSE-V1::GATEWAY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb edge-core org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v1/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v1/gateway/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 100 servicecomb: service: application: demo-java-chassis-cse-v1 name: gateway version: 0.0.1 registry: sc: address: https://cse.cn-south-1.myhuaweicloud.com watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com rest: address: 0.0.0.0:9090?sslEnabled=false http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: consumer: prefixSegmentCount: 0 path: "/.*" microserviceName: consumer versionRule: 0.0.0+ matchGroup: canary-header: | matches: - headers: canary: prefix: "" mapper: canary-header: | target: canary: $H{canary} # Configure AK/SK credentials if needed. Default not enabled. credentials: akskEnabled: true accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 ================================================ FILE: demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml ================================================ ./gateway/log/ ================================================ FILE: demo/demo-cse-v1/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-cse-v1 Java Chassis::Demo::CSE-V1 pom org.apache.servicecomb solution-basic org.apache.servicecomb dashboard org.apache.servicecomb darklaunch org.apache.servicecomb registry-service-center org.apache.servicecomb config-cc org.apache.servicecomb servicestage org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core provider provider-canary consumer gateway test-client ================================================ FILE: demo/demo-cse-v1/provider/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v1 3.4.0-SNAPSHOT cse-v1-provider Java Chassis::Demo::CSE-V1::PROVIDER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController { // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return "Hello " + name; } @GetMapping("/sayHelloCanary") public String sayHelloCanary(@RequestParam("name") String name) { return "Hello Canary " + name; } } ================================================ FILE: demo/demo-cse-v1/provider/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v1 name: provider version: 0.0.1 registry: sc: address: https://cse.cn-south-1.myhuaweicloud.com watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com rest: address: 0.0.0.0:9093 # should be same with server.port to use web container # Configure AK/SK credentials if needed. Default not enabled. credentials: akskEnabled: true accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 ================================================ FILE: demo/demo-cse-v1/provider/src/main/resources/log4j2.xml ================================================ ./user/log/ ================================================ FILE: demo/demo-cse-v1/provider-canary/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v1 3.4.0-SNAPSHOT cse-v1-provider-canary Java Chassis::Demo::CSE-V1::PROVIDER CANARY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController { // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return "Hello in canary " + name; } @GetMapping("/sayHelloCanary") public String sayHelloCanary(@RequestParam("name") String name) { return "Hello Canary in canary " + name; } } ================================================ FILE: demo/demo-cse-v1/provider-canary/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v1 name: provider version: 0.0.2 registry: sc: address: https://cse.cn-south-1.myhuaweicloud.com watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com rest: address: 0.0.0.0:9095 # Configure AK/SK credentials if needed. Default not enabled. credentials: akskEnabled: true accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 ================================================ FILE: demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml ================================================ ./user/log/ ================================================ FILE: demo/demo-cse-v1/test-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v1 3.4.0-SNAPSHOT cse-v1-test-client Java Chassis::Demo::CSE-V1::TEST-CLIENT jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface Config { String GATEWAY_URL = "http://localhost:9090"; } ================================================ FILE: demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class ConsumerConfigIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testConfig(); testFooBar(); } @SuppressWarnings("unchecked") private void testConfig() { String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.foo", String.class); TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.bar", String.class); TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/dynamicString?key=cse.v1.test.dynamicString", String.class); TestMgr.check("a,b", result); List listResult = template .getForObject(Config.GATEWAY_URL + "/dynamicArray", List.class); TestMgr.check(2, listResult.size()); TestMgr.check("m", listResult.get(0)); TestMgr.check("n", listResult.get(1)); } private void testFooBar() { String result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); TestMgr.check("bar", result); } } ================================================ FILE: demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHelloWorldFallback(); testHelloWorldNoHeader(); testHelloWorld(); testHelloWorldCanary(); testHelloWorldEmptyProtectionCloseWeight100(); testHelloWorldeEmptyProtectionCloseWeightLess100(); testHelloWorldEmptyProtectionCloseFallback(); testHelloWorldEmptyProtectionCloseWeight100Two(); } private void testHelloWorld() { for (int i = 0; i < 10; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "old"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("Hello World", result); } } private void testHelloWorldCanary() { int oldCount = 0; int newCount = 0; for (int i = 0; i < 20; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "new"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class).getBody(); if (result.equals("Hello Canary World")) { oldCount++; } else if (result.equals("Hello Canary in canary World")) { newCount++; } else { TestMgr.fail("not expected result testHelloWorldCanary"); return; } } double ratio = oldCount / (float) (oldCount + newCount); TestMgr.check(ratio > 0.1 && ratio < 0.3, true); } private void testHelloWorldFallback() { int oldCount = 0; int newCount = 0; for (int i = 0; i < 20; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "fallback"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class).getBody(); if (result.equals("\"Hello Canary World\"")) { oldCount++; } else if (result.equals("\"Hello Canary in canary World\"")) { newCount++; } else { TestMgr.fail("not expected result testHelloWorldCanary"); return; } } double ratio = oldCount / (float) (oldCount + newCount); TestMgr.check(Math.abs(ratio - 0.2) <= 0.1, true); } private void testHelloWorldNoHeader() { int oldCount = 0; int newCount = 0; for (int i = 0; i < 20; i++) { String result = template .getForObject(Config.GATEWAY_URL + "/sayHelloCanary?name=World", String.class); if (result.equals("\"Hello Canary World\"")) { oldCount++; } else if (result.equals("\"Hello Canary in canary World\"")) { newCount++; } else { TestMgr.fail("not expected result testHelloWorldCanary"); return; } } double ratio = oldCount / (float) (oldCount + newCount); TestMgr.check(Double.compare(ratio, 0.5) == 0, true); } private void testHelloWorldEmptyProtectionCloseWeight100() { int failCount = 0; for (int i = 0; i < 20; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "emptyProtectionClose100"); HttpEntity entity = new HttpEntity<>(headers); try { template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class); } catch (Exception e) { failCount++; } } TestMgr.check(failCount == 20, true); } private void testHelloWorldeEmptyProtectionCloseWeightLess100() { int failCount = 0; int succCount = 0; for (int i = 0; i < 20; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "emptyProtectionCloseLess100"); HttpEntity entity = new HttpEntity<>(headers); try { template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class); succCount++; } catch (Exception e) { failCount++; } } TestMgr.check(succCount == 20, true); } private void testHelloWorldEmptyProtectionCloseFallback() { int failCount = 0; int succCount = 0; for (int i = 0; i < 20; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "emptyProtectionCloseFallback"); HttpEntity entity = new HttpEntity<>(headers); try { template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class); succCount++; } catch (Exception e) { failCount++; } } TestMgr.check(succCount == 20, true); } private void testHelloWorldEmptyProtectionCloseWeight100Two() { int failCount = 0; int succCount = 0; for (int i = 0; i < 20; i++) { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "emptyProtectionClose100-2"); HttpEntity entity = new HttpEntity<>(headers); try { template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class); succCount++; } catch (Exception e) { failCount++; } } TestMgr.check(failCount == succCount, true); } } ================================================ FILE: demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } } ================================================ FILE: demo/demo-cse-v1/test-client/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v1 name: test-client version: 0.0.1 registry: sc: address: https://cse.cn-south-1.myhuaweicloud.com watch: false config: client: serverUri: https://cse.cn-south-1.myhuaweicloud.com rest: address: 0.0.0.0:9097 # should be same with server.port to use web container # Configure AK/SK credentials if needed. Default not enabled. credentials: akskEnabled: true accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 ================================================ FILE: demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml ================================================ ./user/log/ ================================================ FILE: demo/demo-cse-v2/README.md ================================================ # 使用微服务引擎2.0的测试用例 * 参考:https://support.huaweicloud.com/devg-cse/cse_devg_0036.html 安装微服务引擎2.0 * 设置环境变量: * PAAS_CSE_SC_ENDPOINT: 注册中心的地址 * PAAS_CSE_CC_ENDPOINT: 配置中心的地址 * 依次启动 provider、consumer、gateway * 在配置中心增加如下配置: * 应用级配置:consumer.yaml。类型为 yaml。 ```yaml cse: v2: test: foo: foo ``` * 自定义配置:priority1.yaml。label信息: public=default 。类型为 yaml。 ```yaml cse: v2: test: priority: v1 common: common ``` * 自定义配置:priority1.yaml。label信息: public=default,extra=default 。类型为 yaml。 ```yaml cse: v2: test: priority: v1 extra: common ``` * 应用级配置:priority2.yaml。类型为 yaml。 ```yaml cse: v2: test: priority: v2 ``` * 服务级配置:priority3.yaml,微服务性选择consumer。类型为 yaml。 ```yaml cse: v2: test: priority: v3 ``` * 应用级配置:consumerApp.yaml,应用选择demo-java-chassis-cse-v2。类型为 yaml。 ```yaml cse: v2: test: priority1: v1 ``` * 服务级配置:consumerService.yaml,微服务性选择consumer。类型为 yaml。 ```yaml cse: v2: test: priority1: v2 ``` * 应用级配置: cse.v2.test.bar: bar 。 类型为 text。 * 执行 tests-client 里面的集成测试用例 (成功) * 修改 * priority1.yaml。label信息: public=default 。类型为 yaml。 ```yaml cse: v2: test: priority: v4 ``` * 执行 tests-client 里面的集成测试用例 (成功) * 修改 * 应用级priority3.yaml。 ```yaml cse: v2: test: priority: v5 ``` * 执行 tests-client 里面的集成测试用例 (失败) * 修改 * 应用级priority3.yaml。label信息: ```yaml cse: v2: test: priority: v3 ``` * 执行 tests-client 里面的集成测试用例 (成功) * 修改 * 应用级consumerApp.yaml。 ```yaml cse: v2: test: priority1: v10 ``` * 执行 tests-client 里面的集成测试用例 (成功) * 修改 * 服务级配置:consumerService.yaml。 ```yaml cse: v2: test: priority1: v20 ``` * 执行 tests-client 里面的集成测试用例 (成功) * 修改 * 版本级配置:consumerIns.yaml。 ```yaml cse: v2: test: priority1: v30 ``` * 执行 tests-client 里面的集成测试用例 (失败) ================================================ FILE: demo/demo-cse-v2/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v2 3.4.0-SNAPSHOT cse-v2-consumer Java Chassis::Demo::CSE-V2::CONSUMER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ConsumerConfigController") @RequestMapping(path = "/") public class ConsumerConfigController { private Environment environment; private ConsumerConfigurationProperties consumerConfigurationProperties; @Autowired public ConsumerConfigController(Environment environment, ConsumerConfigurationProperties consumerConfigurationProperties) { this.environment = environment; this.consumerConfigurationProperties = consumerConfigurationProperties; } @GetMapping("/config") public String config(@RequestParam("key") String key) { return environment.getProperty(key); } @GetMapping("/foo") public String foo() { return consumerConfigurationProperties.getFoo(); } @GetMapping("/bar") public String bar() { return consumerConfigurationProperties.getBar(); } @GetMapping("/priority") public String priority() { return consumerConfigurationProperties.getPriority(); } @GetMapping("/common") public String common() { return consumerConfigurationProperties.getCommon(); } @GetMapping("/extra") public String extra() { return consumerConfigurationProperties.getExtra(); } } ================================================ FILE: demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @ConfigurationProperties("cse.v2.test") @Component public class ConsumerConfigurationProperties { private String foo; private String bar; private String priority; private String common; private String extra; public String getFoo() { return foo; } public void setFoo(String foo) { this.foo = foo; } public String getBar() { return bar; } public void setBar(String bar) { this.bar = bar; } public String getPriority() { return priority; } public void setPriority(String priority) { this.priority = priority; } public String getCommon() { return common; } public void setCommon(String common) { this.common = common; } public String getExtra() { return extra; } public void setExtra(String extra) { this.extra = extra; } } ================================================ FILE: demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ConsumerController") @RequestMapping(path = "/") public class ConsumerController { @RpcReference(schemaId = "ProviderController", microserviceName = "provider") private ProviderService providerService; // consumer service which delegate the implementation to provider service. @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } } ================================================ FILE: demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface ProviderService { String sayHello(String name); } ================================================ FILE: demo/demo-cse-v2/consumer/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v2 name: consumer version: 0.0.1 registry: sc: address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} watch: false kie: serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public customLabelValue: default rest: address: 0.0.0.0:9092 # should be same with server.port to use web container ================================================ FILE: demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml ================================================ ./file/log/ ================================================ FILE: demo/demo-cse-v2/gateway/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v2 3.4.0-SNAPSHOT cse-v2-gateway Java Chassis::Demo::CSE-V2::GATEWAY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb edge-core org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v2/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v2/gateway/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # override common configurations in common module servicecomb-config-order: 100 servicecomb: service: application: demo-java-chassis-cse-v2 name: gateway version: 0.0.1 registry: sc: address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} watch: false kie: serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public rest: address: 0.0.0.0:9090?sslEnabled=false http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: consumer: prefixSegmentCount: 0 path: "/.*" microserviceName: consumer versionRule: 0.0.0+ ================================================ FILE: demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml ================================================ ./gateway/log/ ================================================ FILE: demo/demo-cse-v2/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-cse-v2 Java Chassis::Demo::CSE-V2 pom org.apache.servicecomb solution-basic org.apache.servicecomb registry-service-center org.apache.servicecomb config-kie org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core provider consumer gateway test-client ================================================ FILE: demo/demo-cse-v2/provider/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v2 3.4.0-SNAPSHOT cse-v2-provider Java Chassis::Demo::CSE-V2::PROVIDER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController { // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return "Hello " + name; } } ================================================ FILE: demo/demo-cse-v2/provider/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations server: port: 9093 # should be same with servicecomb.rest.address to use web container # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v2 name: provider version: 0.0.1 registry: sc: address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} watch: false kie: serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public rest: address: 0.0.0.0:9093 # should be same with server.port to use web container ================================================ FILE: demo/demo-cse-v2/provider/src/main/resources/log4j2.xml ================================================ ./user/log/ ================================================ FILE: demo/demo-cse-v2/test-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-cse-v2 3.4.0-SNAPSHOT cse-v2-test-client Java Chassis::Demo::CSE-V2::TEST-CLIENT jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface Config { String GATEWAY_URL = "http://localhost:9090"; } ================================================ FILE: demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class ConsumerConfigIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testConfig(); testFooBar(); } private void testConfig() { String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.foo", String.class); TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.bar", String.class); TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority", String.class); TestMgr.check("v1", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.common", String.class); TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.extra", String.class); TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority1", String.class); TestMgr.check("v2", result); } private void testFooBar() { String result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/priority", String.class); TestMgr.check("v1", result); result = template.getForObject(Config.GATEWAY_URL + "/common", String.class); TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/extra", String.class); TestMgr.check("common", result); } } ================================================ FILE: demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHelloWorld(); } private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); TestMgr.check("Hello World", result); } } ================================================ FILE: demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } } ================================================ FILE: demo/demo-cse-v2/test-client/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations server: port: 9093 # should be same with servicecomb.rest.address to use web container # override common configurations in common module servicecomb-config-order: 10 servicecomb: service: application: demo-java-chassis-cse-v2 name: test-client version: 0.0.1 registry: sc: address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} watch: false kie: serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} customLabel: public rest: address: 0.0.0.0:9097 # should be same with server.port to use web container ================================================ FILE: demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml ================================================ ./user/log/ ================================================ FILE: demo/demo-edge/README.md ================================================ # About edge service API compatibility * Edge service use the latest version of the microservice meta. e.g. For business 1.0.0, 1.1.0, 2.0.0 have the following APIs: * 1.0.0: /business/v1/add * 1.1.0: /business/v1/add, /business/v1/dec * 2.0.0: /business/v2/add, /business/v2/dec If users invoke /business/v1/add, edge service will give NOT FOUND, because 2.0.0 microservice meta do not have this API. Even using router to route all /business/v1/* requests to 1.1.0, path locating happens before load balance. * It's very important to keep your API compatibility cross versions if these versions need work together. e.g. * 1.0.0: /business/v1/add * 1.1.0: /business/v1/add, /business/v1/dec * 2.0.0: /business/v1/add, /business/v1/dec, /business/v2/add, /business/v2/dec Together with router, /business/v1/add will go correctly to 1.0.0 or 1.1.0, and /business/v2/add will go correctly to 2.0.0. Without router, /business/v2/add may route to 1.0.0 or 1.1.0 and NOT FOUND is reported. ================================================ FILE: demo/demo-edge/authentication/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT authentication Java Chassis::Demo::Edge::Authentication org.apache.servicecomb.demo.edge.authentication.AuthMain org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.authentication; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "auth") @RequestMapping(path = "auth/v1") public class AuthImpl { @PostMapping(path = "/auth") public boolean auth(@RequestHeader(name = "info") String info) { return true; } } ================================================ FILE: demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.authentication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class AuthMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(AuthMain.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/EncryptImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.authentication.encrypt; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "encrypt") @RequestMapping(path = "auth/v1") public class EncryptImpl { @GetMapping(path = "/queryUserId") public String queryUserId(String serviceToken) { return serviceToken + "-userId"; } @GetMapping(path = "/queryHcr") public Hcr queryHcr(String hcrId) { Hcr hcr = new Hcr(); hcr.setBodyKey("bodyKey-" + hcrId + "-"); hcr.setSignatureKey("signatureKey-" + hcrId + "-"); return hcr; } } ================================================ FILE: demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.authentication.encrypt; public class Hcr { private String bodyKey; private String signatureKey; public String getBodyKey() { return bodyKey; } public void setBodyKey(String bodyKey) { this.bodyKey = bodyKey; } public String getSignatureKey() { return signatureKey; } public void setSignatureKey(String signatureKey) { this.signatureKey = signatureKey; } } ================================================ FILE: demo/demo-edge/authentication/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-edge/authentication/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: edge name: auth version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:7070?protocol=http2 server: http2: useAlpnEnabled: false # for test case run in one core machine verticle-count: 1 ================================================ FILE: demo/demo-edge/business-1-1-0/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT business-1-1-0 Java Chassis::Demo::Edge::Business 1.1.0 org.apache.servicecomb.demo.edge.business.BusinessMain_V1_1_0 org.apache.servicecomb.demo model ${project.version} org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_1_0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class BusinessMain_V1_1_0 { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BusinessMain_V1_1_0.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RestSchema(schemaId = "news-v1") @RequestMapping(path = "/business/v1") public class Impl { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); String rsp = "result from 1.1.0"; response.setRsp(rsp); return response; } @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { return ResultWithInstance.create(x + y, environment); } @RequestMapping(path = "/dec", method = RequestMethod.GET) public ResultWithInstance dec(int x, int y) { return ResultWithInstance.create(x - y, environment); } } ================================================ FILE: demo/demo-edge/business-1-1-0/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-edge/business-1-1-0/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: edge name: business version: 1.1.0 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8090 server: verticle-count: 1 ================================================ FILE: demo/demo-edge/business-1.0.0/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT business-1-0-0 Java Chassis::Demo::Edge::Business 1.0.0 org.apache.servicecomb.demo.edge.business.BusinessMain_V1_0_0 org.apache.servicecomb.demo model ${project.version} org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_0_0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class BusinessMain_V1_0_0 { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BusinessMain_V1_0_0.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RestSchema(schemaId = "news-v1") @RequestMapping(path = "/business/v1") public class Impl { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); String rsp = "result from 1.0.0"; response.setRsp(rsp); return response; } @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { return ResultWithInstance.create(x + y, environment); } } ================================================ FILE: demo/demo-edge/business-1.0.0/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-edge/business-1.0.0/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: edge name: business version: 1.0.0 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 server: verticle-count: 1 ================================================ FILE: demo/demo-edge/business-2.0.0/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT business-2-0-0 Java Chassis::Demo::Edge::Business 2.0.0 org.apache.servicecomb.demo.edge.business.BusinessMain_V2_0_0 org.apache.servicecomb.demo model ${project.version} org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V2_0_0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class BusinessMain_V2_0_0 { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BusinessMain_V2_0_0.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/EdgeServiceGovernanceService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @RestSchema(schemaId = "EdgeServiceGovernanceService") @RequestMapping(path = "/business/v2") public class EdgeServiceGovernanceService { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeServiceGovernanceService.class); private Map retryTimes = new HashMap<>(); private AtomicInteger instanceIsolationIndex = new AtomicInteger(); @GetMapping("/testEdgeServiceRetry") @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String testEdgeServiceRetry(@RequestParam(name = "invocationID") String invocationID) { LOGGER.info("invoke service: {}", invocationID); retryTimes.putIfAbsent(invocationID, 0); retryTimes.put(invocationID, retryTimes.get(invocationID) + 1); int retry = retryTimes.get(invocationID); if (retry == 3) { return "try times: " + retry; } throw new InvocationException(502, "retry", "retry"); } @GetMapping("/testEdgeServiceInstanceIsolation") @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String testEdgeServiceInstanceIsolation(@RequestParam(name = "name") String name) { if (instanceIsolationIndex.getAndIncrement() % 3 != 0) { throw new InvocationException(502, "InstanceIsolation", "InstanceIsolation"); } return name; } @GetMapping("/testEdgeServiceInstanceBulkhead") public String testEdgeServiceInstanceBulkhead(@RequestParam(name = "name") String name) { return name; } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RestSchema(schemaId = "news-v1") @RequestMapping(path = "/business/v1") public class Impl { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); String rsp = "result from 2.0.0"; response.setRsp(rsp); return response; } @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { return ResultWithInstance.create(x + y, environment); } @RequestMapping(path = "/dec", method = RequestMethod.GET) public ResultWithInstance dec(int x, int y) { return ResultWithInstance.create(x - y, environment); } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.DependTypeA; import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.demo.edge.model.User; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @RestSchema(schemaId = "news-v2") @RequestMapping(path = "/business/v2") public class ImplV2 { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } File tempDir = new File("target/downloadTemp"); public ImplV2() throws IOException { FileUtils.forceMkdir(tempDir); } @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); String rsp = "result from 2.0.0"; response.setRsp(rsp); return response; } @RequestMapping(path = "/add", method = RequestMethod.GET) public ResultWithInstance add(int x, int y) { return ResultWithInstance.create(x + y, environment); } @RequestMapping(path = "/dec", method = RequestMethod.GET) public ResultWithInstance dec(int x, int y) { return ResultWithInstance.create(x - y, environment); } @GetMapping(path = "/download") @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = File.class)), description = ""), }) public ResponseEntity download() throws IOException { return ResponseEntity .ok() .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); } protected File createBigFile() throws IOException { File file = new File(tempDir, "bigFile.txt"); file.delete(); RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); randomAccessFile.setLength(10 * 1024 * 1024); randomAccessFile.close(); return file; } @GetMapping(path = "/bigFile") public File bigFile() throws IOException { return createBigFile(); } @PostMapping(path = "recursiveSelf") public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { return value; } @PostMapping(path = "dependType") public DependTypeA dependType(@RequestBody DependTypeA value) { return value; } @PostMapping(path = "encrypt") public User encrypt(@RequestBody User value) { return value; } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/CustomExceptionToProducerResponseConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business.error; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; public class CustomExceptionToProducerResponseConverter implements ExceptionConverter { @Override public boolean canConvert(Throwable throwable) { return throwable instanceof IllegalStateException; } @Override public int getOrder() { return 100; } @Override public InvocationException convert(Invocation invocation, IllegalStateException e, StatusType genericStatus) { IllegalStateErrorData data = new IllegalStateErrorData(); data.setId(500); data.setMessage(e.getMessage()); data.setState(e.getMessage()); return new InvocationException(Status.INTERNAL_SERVER_ERROR, data); } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business.error; public class ErrorData { private int id; private String message; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business.error; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RestSchema(schemaId = "error-v2") @RequestMapping(path = "/business/v2/error") public class ErrorService { @RequestMapping(path = "/add", method = RequestMethod.GET) public int add(int x, int y) { if (x == 99) { throw new NullPointerException("un expected NPE test."); } if (x == 88) { ErrorData data = new ErrorData(); data.setId(12); data.setMessage("not allowed id."); throw new InvocationException(Status.FORBIDDEN, data); } if (x == 77) { throw new IllegalStateException("77"); } return x + y; } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/IllegalStateErrorData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.business.error; public class IllegalStateErrorData { private int id; private String message; private String state; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getState() { return state; } public void setState(String state) { this.state = state; } } ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.edge.business.error.CustomExceptionToProducerResponseConverter ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-edge/business-2.0.0/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: edge name: business version: 2.0.0 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8091 server: verticle-count: 1 ================================================ FILE: demo/demo-edge/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT consumer Java Chassis::Demo::Edge::Consumer org.apache.servicecomb.demo.edge.consumer.ConsumerMain org.apache.servicecomb.demo demo-schema org.apache.servicecomb.demo model ${project.version} docker io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 authentication:${project.version} authentication -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/authentication-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 7070 7070:7070 service-center business-1-0-0:${project.version} business-1-0-0 -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/business-1-0-0-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 8080:8080 service-center business-1-1-0:${project.version} business-1-1-0 -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/business-1-1-0-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8090 8090:8090 service-center business-2-0-0:${project.version} business-2-0-0 -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/business-2-0-0-${project.version}.jar service-center:sc.servicecomb.io Register microservice instance success 8091 8091:8091 service-center edge-service:${project.version} edge-service -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/edge-service-${project.version}.jar service-center:sc.servicecomb.io Register microservice instance success 18090 18090:18090 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.consumer; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.DependTypeA; import org.apache.servicecomb.demo.edge.model.DependTypeB; import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; public class Consumer { RestOperations template = RestTemplateBuilder.create(); ChannelRequestBase request = new ChannelRequestBase(); String edgePrefix; List addV1Result = new ArrayList<>(); List decV1Result = new ArrayList<>(); List addV2Result = new ArrayList<>(); List decV2Result = new ArrayList<>(); DiscoveryManager discoveryManager = BeanUtils.getBean(DiscoveryManager.class); Environment environment = BeanUtils.getBean(Environment.class); public Consumer() { request.setDeviceId("2a5cc42ff60006ac"); request.setServiceToken( "c2VydmliZVRva2VuPTAwMjAwMDg2MDAwMDAwMDAwODMyYzYzamV1ZnV1cWdpYXgmRGV2aWNIVHIw" + "ZT0wJkRLdmljZUIEPTg2MzgoMDAyMDA0NDcwMiZhcHBJRD1jb20uaHVhd2VpLndhbGxldA"); request.setPhoneType("VTR-AL00"); request.setUserId("20086000000000832"); request.setCmdId("5"); request.setNet("1"); request.setUserGrant("000"); request.setSysVer("EMUI5.1"); request.setTs("1497356427334"); request.setChannelId("3"); request.setLocation(null); request.setCmdVer("2.0"); request.setLanguage("zh_CN"); } public void run(String prefix) { prepareEdge(prefix); testRecursiveSelf(); testDependType(); testDownload(); testDownloadBigFile(); testErrorCode(); invoke("/v1/add", 2, 1, addV1Result); invoke("/v1/add", 3, 1, addV1Result); invoke("/v1/add", 4, 1, addV1Result); invoke("/v1/add", 5, 1, addV1Result); invoke("/v1/dec", 2, 1, decV1Result); invoke("/v1/dec", 3, 1, decV1Result); invoke("/v2/add", 2, 1, addV2Result); invoke("/v2/add", 3, 1, addV2Result); invoke("/v2/dec", 2, 1, decV2Result); invoke("/v2/dec", 3, 1, decV2Result); printResults("v1/add", addV1Result); printResults("v1/dec", decV1Result); printResults("v2/add", addV2Result); printResults("v2/dec", decV2Result); checkResult("v1/add", addV1Result, "1.0.0", "1.1.0"); checkResult("v1/dec", decV1Result, "1.1.0"); checkResult("v2/add", addV2Result, "2.0.0"); checkResult("v2/dec", decV2Result, "2.0.0"); } public void testEncrypt() { prepareEdge("encryptApi"); String url = edgePrefix + "/v2/encrypt"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap form = new LinkedMultiValueMap<>(); form.add("name", "userName"); form.add("age", "10"); form.add("serviceToken", "serviceTokenTest"); form.add("hcrId", "hcrIdTest"); form.add("body", "bodyKey-hcrIdTest-{\"body1\":\"b1\",\"body2\":\"b2\",\"body3\":\"b3\"}"); HttpEntity> entity = new HttpEntity<>(form, headers); @SuppressWarnings("unchecked") Map result = (Map) template.postForObject(url, entity, Map.class); Assert.isTrue(result.containsKey("signature"), "must exist signature"); result.remove("signature"); String expected = "{name=userName, age=10, userId=serviceTokenTest-userId, body1=b1, body2=b2, body3=b3}"; Assert.isTrue(expected.equalsIgnoreCase(result.toString()), String.format("expected: %s\nreal : %s", expected, result)); } protected void testRecursiveSelf() { String url = edgePrefix + "/v2/recursiveSelf"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); RecursiveSelfType recursiveSelfType = new RecursiveSelfType(); recursiveSelfType.setField(new RecursiveSelfType()); recursiveSelfType.getField().setValue(10); HttpEntity entity = new HttpEntity<>(recursiveSelfType, headers); RecursiveSelfType response = template.postForObject(url, entity, RecursiveSelfType.class); Assert.isTrue(response.getValue() == 0, "default must be 0"); Assert.isTrue(response.getField().getValue() == 10, "must be 10"); Assert.isNull(response.getField().getField(), "must be null"); } @SuppressWarnings({"rawtypes"}) protected void testErrorCode() { String url = edgePrefix + "/v2/error/add"; int response = template.getForObject(url + "?x=2&y=3", Integer.class); Assert.isTrue(response == 5, "not get 5."); try { Map raw = template.getForObject(url + "?x=99&y=3", Map.class); } catch (HttpServerErrorException e) { Assert.isTrue(e.getStatusCode().value() == 500, "x99"); Assert.isTrue(e.getResponseBodyAsString().contains("un expected NPE test."), "x99"); } try { template.getForObject(url + "?x=88&y=3", Map.class); Assert.isTrue(false, "x88"); } catch (HttpClientErrorException e) { Assert.isTrue(e.getStatusCode().value() == 403, "x88"); Assert.isTrue(e.getResponseBodyAsString().equals("{\"id\":12,\"message\":\"not allowed id.\"}"), "x88"); } try { template.getForObject(url + "?x=77&y=3", Map.class); Assert.isTrue(false, "x77"); } catch (HttpServerErrorException e) { Assert.isTrue(e.getStatusCode().value() == 500, "x77"); Assert.isTrue(e.getResponseBodyAsString().equals("{\"id\":500,\"message\":\"77\",\"state\":\"77\"}"), "x77"); } } protected void testDependType() { String url = edgePrefix + "/v2/dependType"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); DependTypeA dependTypeA = new DependTypeA(); dependTypeA.setB(new DependTypeB()); dependTypeA.getB().setValue(10); HttpEntity entity = new HttpEntity<>(dependTypeA, headers); DependTypeA response = template.postForObject(url, entity, DependTypeA.class); Assert.isTrue(response.getB().getValue() == 10, "must be 10"); } protected void testDownloadBigFile() { String url = edgePrefix + "/v2/bigFile"; AtomicInteger size = new AtomicInteger(); template.execute(url, HttpMethod.GET, req -> { }, resp -> { byte[] buf = new byte[1 * 1024 * 1024]; try (InputStream is = resp.getBody()) { for (; ; ) { int len = is.read(buf); if (len == -1) { break; } size.addAndGet(len); } } return null; }); Assert.isTrue(size.get() == 10 * 1024 * 1024, "size is : " + size.get() + " not 10 * 1024 * 1024"); System.out.println("test download bigFile finished"); } protected void testDownload() { String url = edgePrefix + "/v2/download"; String content = template.getForObject(url, String.class); Assert.isTrue("download".equals(content), "content is : " + content + " not download"); System.out.println("test download finished"); } private void checkResult(String name, List results, String... expectedVersions) { Set versions = new HashSet<>(); Set remained = new HashSet<>(Arrays.asList(expectedVersions)); for (ResultWithInstance result : results) { versions.add(result.getVersion()); remained.remove(result.getVersion()); } Assert.isTrue(remained.isEmpty(), String.format("%s expectedVersions %s, real versions %s.", name, Arrays.deepToString(expectedVersions), versions)); } protected void printResults(String name, List results) { System.out.println(name); for (ResultWithInstance result : results) { System.out.println(result); } System.out.println(""); } protected void invoke(String appendUrl, int x, int y, List results) { String url = edgePrefix + appendUrl + String.format("?x=%d&y=%d", x, y); ResultWithInstance result = template.getForObject(url, ResultWithInstance.class); results.add(result); } private URIEndpointObject prepareEdge(String prefix) { DiscoveryInstance instance = discoveryManager.findServiceInstances( BootStrapProperties.readApplication(environment), "edge") .stream() .findFirst() .get(); URIEndpointObject edgeAddress = new URIEndpointObject(instance.getEndpoints().get(0)); edgePrefix = String.format("http://%s:%d/%s/business", edgeAddress.getHostOrIp(), edgeAddress.getPort(), prefix); return edgeAddress; } protected void invokeBusiness(String urlPrefix, ChannelRequestBase request) { List result = new ArrayList<>(6); for (int i = 0; i < 6; i++) { String url = urlPrefix + "/channel/news/subscribe"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity<>(request, headers); ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); result.add(response.getBody().getRsp()); } Assert.isTrue(result.contains("result from 2.0.0"), "invokeBusiness not balance"); Assert.isTrue(result.contains("result from 1.1.0"), "invokeBusiness not balance"); Assert.isTrue(result.contains("result from 1.0.0"), "invokeBusiness not balance"); } } ================================================ FILE: demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.consumer; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsumerMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(ConsumerMain.class).web(WebApplicationType.NONE).run(args); runTest(); } public static void runTest() throws Exception { new Consumer().testEncrypt(); new Consumer().invokeBusiness("cse://business/business/v1", new ChannelRequestBase()); System.out.println("Running api dispatcher."); new Consumer().run("api"); System.out.println("Running rest dispatcher."); new Consumer().run("rest"); System.out.println("Running url dispatcher."); new Consumer().run("url"); // Common Http Dispatcher do not have OperationMeta, can not use router. // System.out.println("Running http dispatcher."); // new Consumer().run("http"); System.out.println("All test case finished."); runCategorizedTest(); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { throw new IllegalStateException("tests failed"); } } private static void runCategorizedTest() throws Exception { CategorizedTestCaseRunner .runCategorizedTestCase("edge"); } } ================================================ FILE: demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.consumer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; @Component public class EdgeServiceGovernanceTest implements CategorizedTestCase { RestOperations template = RestTemplateBuilder.create(); String edgePrefix; @Autowired Environment environment; @Autowired DiscoveryManager discoveryManager; @Override public void testRestTransport() throws Exception { prepareEdge("url"); // edge service do not support retry // testEdgeServiceRetry(); testEdgeServiceInstanceBulkhead(); testEdgeServiceInstanceIsolation(); // may isolate instance for 5 seconds. Thread.sleep(6000); // ensure isolation is open for new requests } private void testEdgeServiceInstanceBulkhead() throws Exception { String url = edgePrefix + "/business/v2/testEdgeServiceInstanceBulkhead"; CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed503 = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { String result = template.getForObject(url + "?name={1}", String.class, "hello"); if (!"hello".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { if (!(e instanceof HttpServerErrorException)) { notExpectedFailed.set(true); } else { if (((HttpServerErrorException) e).getStatusCode().value() == 503) { expectedFailed503.set(true); } } } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(true, expectedFailed503.get()); TestMgr.check(false, notExpectedFailed.get()); } private void testEdgeServiceInstanceIsolation() throws Exception { String url = edgePrefix + "/business/v2/testEdgeServiceInstanceIsolation"; CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed404 = new AtomicBoolean(false); AtomicBoolean expectedFailed502 = new AtomicBoolean(false); AtomicBoolean expectedFailed503 = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { String result = template.getForObject(url + "?name={1}", String.class, "hello"); if (!"hello".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { if (e instanceof HttpClientErrorException) { // isolate 2.0.0 and other instance will give 404 if (((HttpClientErrorException) e).getStatusCode().value() == 404) { expectedFailed404.set(true); } else { notExpectedFailed.set(true); } } else if (e instanceof HttpServerErrorException) { // instance isolated and return 503 if (((HttpServerErrorException) e).getStatusCode().value() == 503) { expectedFailed503.set(true); } // provider throw 502 exception to trigger instance isolation if (((HttpServerErrorException) e).getStatusCode().value() == 502) { expectedFailed502.set(true); } } else { notExpectedFailed.set(true); } } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(true, expectedFailed404.get()); TestMgr.check(true, expectedFailed502.get()); TestMgr.check(true, expectedFailed503.get()); TestMgr.check(false, notExpectedFailed.get()); } private URIEndpointObject prepareEdge(String prefix) { DiscoveryInstance microserviceInstance = discoveryManager.findServiceInstances( BootStrapProperties.readApplication(environment), "edge") .stream() .findFirst() .get(); URIEndpointObject edgeAddress = new URIEndpointObject(microserviceInstance.getEndpoints().get(0)); edgePrefix = String.format("http://%s:%d/%s", edgeAddress.getHostOrIp(), edgeAddress.getPort(), prefix); return edgeAddress; } } ================================================ FILE: demo/demo-edge/consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-edge/consumer/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: edge name: consumer version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: client.http2.useAlpnEnabled: false handler: chain: Consumer: default: loadbalance ================================================ FILE: demo/demo-edge/consumer/src/test/java/org/apache/servicecomb/demo/edge/consumer/EdgeDemoIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.consumer; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = ConsumerMain.class) public class EdgeDemoIT { @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { ConsumerMain.runTest(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-edge/edge-service/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT edge-service Java Chassis::Demo::Edge::Service org.apache.servicecomb.demo.edge.EdgeMain org.apache.servicecomb edge-core org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/EdgeMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class EdgeMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(EdgeMain.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.authentication.encrypt; public class Hcr { private String bodyKey; private String signatureKey; public String getBodyKey() { return bodyKey; } public void setBodyKey(String bodyKey) { this.bodyKey = bodyKey; } public String getSignatureKey() { return signatureKey; } public void setSignatureKey(String signatureKey) { this.signatureKey = signatureKey; } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/CustomResponseMetaMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.SimpleType; public class CustomResponseMetaMapper implements ResponseMetaMapper { private static final Map CODES = new HashMap<>(1); static { CODES.put(500, SimpleType.constructUnsafe(IllegalStateErrorData.class)); } @Override public int getOrder() { return 100; } @Override public Map getMapper() { return CODES; } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service; public interface EdgeConst { String ENCRYPT_CONTEXT = "encryptContext"; } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service; import java.util.Map; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.edge.core.AbstractEdgeDispatcher; import org.apache.servicecomb.edge.core.EdgeInvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; public class EdgeDispatcher extends AbstractEdgeDispatcher { @Override public int getOrder() { return 10000; } @Override public void init(Router router) { String regex = "/api/([^\\\\/]+)/([^\\\\/]+)/(.*)"; // cookies handler are enabled by default start from 3.8.3 router.routeWithRegex(regex).handler(createBodyHandler()); router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest); } protected void onRequest(RoutingContext context) { Map pathParams = context.pathParams(); String microserviceName = pathParams.get("param0"); String path = context.request().path().substring(4); requestByFilter(context, microserviceName, path); } protected void requestByFilter(RoutingContext context, String microserviceName, String path) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, microserviceName, path); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/IllegalStateErrorData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service; public class IllegalStateErrorData { private int id; private String message; private String state; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getState() { return state; } public void setState(String state) { this.state = state; } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/Encrypt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; public interface Encrypt { CompletableFuture queryUserId(String serviceToken); CompletableFuture queryHcr(String hcrId); } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; public class EncryptContext { private Hcr hcr; private String userId; public EncryptContext(Hcr hcr, String userId) { this.hcr = hcr; this.userId = userId; } public Hcr getHcr() { return hcr; } public void setHcr(Hcr hcr) { this.hcr = hcr; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.edge.core.AbstractEdgeDispatcher; import org.apache.servicecomb.edge.core.EdgeInvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import org.apache.servicecomb.provider.pojo.Invoker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; public class EncryptEdgeDispatcher extends AbstractEdgeDispatcher { private static final Logger LOGGER = LoggerFactory.getLogger(EncryptEdgeDispatcher.class); private Encrypt encrypt = Invoker.createProxy("auth", "encrypt", Encrypt.class); private String prefix = "encryptApi"; @Override public int getOrder() { return 10000; } @Override public void init(Router router) { String regex = "/" + prefix + "/([^\\\\/]+)/(.*)"; // cookies handler are enabled by default start from 3.8.3 router.routeWithRegex(regex).handler(createBodyHandler()); router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest); } protected void onRequest(RoutingContext context) { HttpServerRequest httpServerRequest = context.request(); // queryUserId always success CompletableFuture userIdFuture = queryUserId(httpServerRequest); queryHcr(httpServerRequest).thenCombine(userIdFuture, (hcr, userId) -> { // hcr and userId all success routeToBackend(context, hcr, userId); return null; }).whenComplete((v, e) -> { // failed to query hcr if (e != null) { context.response().end("failed to query hcr: " + e.getMessage()); return; } }); } private CompletableFuture queryUserId(HttpServerRequest httpServerRequest) { String serviceToken = httpServerRequest.getParam("serviceToken"); if (serviceToken == null) { // no need to query userId return CompletableFuture.completedFuture(null); } CompletableFuture future = new CompletableFuture<>(); encrypt.queryUserId(serviceToken).whenComplete((userId, e) -> { if (e != null) { // treat as success, just userId is null LOGGER.error("Failed to query userId, serviceToken={}.", serviceToken, e); } future.complete(userId); }); return future; } private CompletableFuture queryHcr(HttpServerRequest httpServerRequest) { String hcrId = httpServerRequest.getParam("hcrId"); return encrypt.queryHcr(hcrId); } private void routeToBackend(RoutingContext context, Hcr hcr, String userId) { Map pathParams = context.pathParams(); String microserviceName = pathParams.get("param0"); String path = context.request().path().substring(prefix.length() + 1); requestByFilter(context, microserviceName, path, new EncryptContext(hcr, userId)); } protected void requestByFilter(RoutingContext context, String microserviceName, String path , EncryptContext encryptContext) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, microserviceName, path) { @Override public CompletableFuture createAsync() { CompletableFuture result = super.createAsync(); return result.whenComplete((invocation, throwable) -> { if (throwable == null) { invocation.getHandlerContext().put(EdgeConst.ENCRYPT_CONTEXT, encryptContext); } }); } }; new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt.filter; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; @Component public class DecodeBodyFilter extends AbstractFilter implements EdgeFilter { private JavaType bodyType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String[].class); @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; } @Override public String getName() { return "test-edge-decode-body"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); if (encryptContext == null) { return nextNode.onFilter(invocation); } Hcr hcr = encryptContext.getHcr(); String encodedBody = invocation.getRequestEx().getParameter("body"); if (encodedBody == null) { return nextNode.onFilter(invocation); } encodedBody = encodedBody.substring(hcr.getBodyKey().length()); try { Map decodedBody = RestObjectMapperFactory.getRestObjectMapper() .readValue(encodedBody, bodyType); invocation.getRequestEx().getParameterMap().putAll(decodedBody); } catch (Throwable e) { // should be a meaning exception response return CompletableFuture.failedFuture(e); } return nextNode.onFilter(invocation); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt.filter; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; @Component public class EdgeSignatureRequestFilter extends AbstractFilter implements EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureRequestFilter.class); @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1800; } @Override public String getName() { return "test-edge-signature-request"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); if (encryptContext == null) { return nextNode.onFilter(invocation); } Hcr hcr = encryptContext.getHcr(); // signature for query and form List names = Collections.list(invocation.getRequestEx().getParameterNames()); names.sort(Comparator.naturalOrder()); Hasher hasher = Hashing.sha256().newHasher(); hasher.putString(hcr.getSignatureKey(), StandardCharsets.UTF_8); for (String name : names) { hasher.putString(name, StandardCharsets.UTF_8); hasher.putString(invocation.getRequestEx().getParameter(name), StandardCharsets.UTF_8); } LOGGER.info("afterReceiveRequest signature: {}", hasher.hash()); return nextNode.onFilter(invocation); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt.filter; import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import io.vertx.core.buffer.Buffer; @Component public class EdgeSignatureResponseFilter extends AbstractFilter implements EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureResponseFilter.class); @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1991; } @Override public String getName() { return "test-edge-signature-response"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return nextNode.onFilter(invocation).whenComplete((response, throwable) -> { if (throwable != null) { return; } EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); if (encryptContext == null) { return; } Hcr hcr = encryptContext.getHcr(); // bad practice: it's better to set signature in response header Buffer bodyBuffer = response.getResult(); String body = bodyBuffer.toString(); if (body.endsWith("}")) { Hasher hasher = Hashing.sha256().newHasher(); hasher.putString(hcr.getSignatureKey(), StandardCharsets.UTF_8); hasher.putString(body, StandardCharsets.UTF_8); String signature = hasher.hash().toString(); LOGGER.info("beforeSendResponse signature: {}", signature); body = body.substring(0, body.length() - 1) + ",\"signature\":\"" + signature + "\"}"; response.setResult(Buffer.buffer(body)); } }); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.encrypt.filter; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @Component public class UserIdFilter extends AbstractFilter implements EdgeFilter { @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; } @Override public String getName() { return "test-edge-user-id"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); if (encryptContext == null) { return nextNode.onFilter(invocation); } String userId = encryptContext.getUserId(); if (userId != null) { invocation.getRequestEx().setParameter("userId", userId); } return nextNode.onFilter(invocation); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/Auth.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.handler; import java.util.concurrent.CompletableFuture; public interface Auth { CompletableFuture auth(String info); } ================================================ FILE: demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.service.handler; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import jakarta.ws.rs.core.Response.Status; @Component public class AuthHandler extends AbstractFilter implements EdgeFilter { private static Logger LOGGER = LoggerFactory.getLogger(AuthHandler.class); private static Auth auth; static { auth = Invoker.createProxy("auth", "auth", Auth.class); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1980; } @Override public boolean enabledForMicroservice(String application, String serviceName) { // Do not enable for service auth and not enable for internal management access(myself) return !"auth".equals(serviceName) && !"edge".equals(serviceName); } @Override public String getName() { return "test-auth"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT) != null) { return nextNode.onFilter(invocation); } return auth.auth("").thenCompose(result -> doHandle(invocation, nextNode, result)); } protected CompletableFuture doHandle(Invocation invocation, FilterNode nextNode, Boolean authSucc) { if (!authSucc) { return CompletableFuture.failedFuture(new InvocationException(Status.UNAUTHORIZED, (Object) "auth failed")); } LOGGER.debug("auth success."); return nextNode.onFilter(invocation); } } ================================================ FILE: demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.edge.service.CustomResponseMetaMapper ================================================ FILE: demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.edge.service.EdgeDispatcher org.apache.servicecomb.demo.edge.service.encrypt.EncryptEdgeDispatcher ================================================ FILE: demo/demo-edge/edge-service/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-edge/edge-service/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: edge name: edge version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 edge: filter: addHeader: enabled: true allowedHeaders: xxx rest: address: 0.0.0.0:18090 server: # for test case run in one core machine verticle-count: 1 client: verticle-count: 1 http2.useAlpnEnabled: false handler: chain: Consumer: default: auth,loadbalance,instance-isolation-consumer,instance-bulkhead-consumer service: auth: loadbalance,instance-isolation-consumer,instance-bulkhead-consumer http: dispatcher: edge: default: enabled: true prefix: rest withVersion: true prefixSegmentCount: 1 url: enabled: true mappings: businessV1: prefixSegmentCount: 1 path: "/url/business/v1/.*" microserviceName: business businessV2: prefixSegmentCount: 1 path: "/url/business/v2/.*" microserviceName: business http: enabled: true mappings: businessV2: prefixSegmentCount: 1 path: "/http/business/v2/.*" microserviceName: business businessV1: prefixSegmentCount: 1 path: "/http/business/v1/add.*" microserviceName: business businessV1_1: prefixSegmentCount: 1 path: "/http/business/v1/dec.*" microserviceName: business # 服务治理配置 matchGroup: testEdgeServiceRetry: | matches: - apiPath: exact: "/business/v2/testEdgeServiceRetry" testEdgeServiceInstanceIsolation: | matches: - apiPath: exact: "/business/v2/testEdgeServiceInstanceIsolation" testEdgeServiceInstanceBulkhead: | matches: - apiPath: exact: "/business/v2/testEdgeServiceInstanceBulkhead" # retry not supported now # retry: # testEdgeServiceRetry: | # maxAttempts: 2 # retryOnSame: 0 instanceIsolation: testEdgeServiceInstanceIsolation: | minimumNumberOfCalls: 10 slidingWindowSize: 20 slidingWindowType: COUNT_BASED failureRateThreshold: 50 slowCallRateThreshold: 100 slowCallDurationThreshold: 3000 waitDurationInOpenState: 3000 permittedNumberOfCallsInHalfOpenState: 10 instanceBulkhead: testEdgeServiceInstanceBulkhead: | maxConcurrentCalls: 1 maxWaitDuration: 1 # enable router for edge service router: type: router routeRule: business: | - precedence: 2 match: apiPath: prefix: "/business/v2" route: - weight: 100 tags: version: 2.0.0 - precedence: 1 match: apiPath: prefix: "/business/v1/dec" route: - weight: 50 tags: version: 1.1.0 - weight: 50 tags: version: 2.0.0 ================================================ FILE: demo/demo-edge/model/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-edge 3.4.0-SNAPSHOT model Java Chassis::Demo::Edge::Model org.apache.servicecomb registry-service-center ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/AppClientDataRsp.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; public class AppClientDataRsp { private String rsp; public String getRsp() { return rsp; } public void setRsp(String rsp) { this.rsp = rsp; } @Override public String toString() { return "AppClientDataRsp [rsp=" + rsp + "]"; } } ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ChannelRequestBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; public class ChannelRequestBase { private String deviceId; private String serviceToken; private String phoneType; private String userId; private String cmdId; private String net; private String userGrant; private String sysVer; private String ts; private String channelId; private String location; private String cmdVer; private String version; private String language; public String getDeviceId() { return deviceId; } public void setDeviceId(String deviceId) { this.deviceId = deviceId; } public String getServiceToken() { return serviceToken; } public void setServiceToken(String serviceToken) { this.serviceToken = serviceToken; } public String getPhoneType() { return phoneType; } public void setPhoneType(String phoneType) { this.phoneType = phoneType; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getCmdId() { return cmdId; } public void setCmdId(String cmdId) { this.cmdId = cmdId; } public String getNet() { return net; } public void setNet(String net) { this.net = net; } public String getUserGrant() { return userGrant; } public void setUserGrant(String userGrant) { this.userGrant = userGrant; } public String getSysVer() { return sysVer; } public void setSysVer(String sysVer) { this.sysVer = sysVer; } public String getTs() { return ts; } public void setTs(String ts) { this.ts = ts; } public String getChannelId() { return channelId; } public void setChannelId(String channelId) { this.channelId = channelId; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getCmdVer() { return cmdVer; } public void setCmdVer(String cmdVer) { this.cmdVer = cmdVer; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } @Override public String toString() { return "ChannelRequestBase [deviceId=" + deviceId + ", serviceToken=" + serviceToken + ", phoneType=" + phoneType + ", userId=" + userId + ", cmdId=" + cmdId + ", net=" + net + ", userGrant=" + userGrant + ", sysVer=" + sysVer + ", ts=" + ts + ", channelId=" + channelId + ", location=" + location + ", cmdVer=" + cmdVer + ", version=" + version + ", language=" + language + "]"; } } ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; public class DependTypeA { private DependTypeB b; public DependTypeB getB() { return b; } public void setB(DependTypeB b) { this.b = b; } } ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; public class DependTypeB { private DependTypeA a; private int value; public DependTypeA getA() { return a; } public void setA(DependTypeA a) { this.a = a; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; public class RecursiveSelfType { private RecursiveSelfType field; private int value; public RecursiveSelfType getField() { return field; } public void setField(RecursiveSelfType field) { this.field = field; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ResultWithInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; import org.apache.servicecomb.config.BootStrapProperties; import org.springframework.core.env.Environment; public class ResultWithInstance { private int result; private String serviceId; private String instanceId; private String version; public static ResultWithInstance create(int value, Environment environment) { ResultWithInstance result = new ResultWithInstance(); result.setResult(value); result.setVersion(BootStrapProperties.readServiceVersion(environment)); return result; } public int getResult() { return result; } public void setResult(int result) { this.result = result; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @Override public String toString() { return "ResultWithInstance [result=" + result + ", serviceId=" + serviceId + ", instanceId=" + instanceId + ", version=" + version + "]"; } } ================================================ FILE: demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.edge.model; public class User { private String name; private int age; private String userId; private String body1; private String body2; private String body3; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getBody1() { return body1; } public void setBody1(String body1) { this.body1 = body1; } public String getBody2() { return body2; } public void setBody2(String body2) { this.body2 = body2; } public String getBody3() { return body3; } public void setBody3(String body3) { this.body3 = body3; } } ================================================ FILE: demo/demo-edge/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-edge Java Chassis::Demo::Edge pom org.apache.servicecomb solution-basic org.apache.servicecomb registry-service-center org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core model edge-service business-1.0.0 business-1-1-0 business-2.0.0 authentication consumer ================================================ FILE: demo/demo-etcd/README.md ================================================ # Notice This integration tests is designed for Etcd registry and configuration. And extra test cases include: * Test cases related to SpringMVC annotations that demo-springmvc can not cover. ================================================ FILE: demo/demo-etcd/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-etcd 3.4.0-SNAPSHOT etcd-consumer Java Chassis::Demo::Etcd::CONSUMER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone com.google.protobuf protobuf-java 3.25.5 runtime org.apache.servicecomb registry-etcd org.apache.servicecomb config-etcd org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.ServerWebSocket; import io.vertx.core.http.WebSocket; @RestSchema(schemaId = "ClientWebsocketController") @RequestMapping(path = "/ws") public class ClientWebsocketController { interface ProviderService { WebSocket websocket(); } @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") private ProviderService providerService; @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) public void websocket(ServerWebSocket serverWebsocket) { WebSocket providerWebSocket = providerService.websocket(); providerWebSocket.closeHandler(v -> serverWebsocket.close()); providerWebSocket.textMessageHandler(m -> { serverWebsocket.writeTextMessage(m); }); serverWebsocket.textMessageHandler(m -> { providerWebSocket.writeTextMessage(m); }); } } ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @RestSchema(schemaId = "ConsumerController") @RequestMapping(path = "/") public class ConsumerController { @RpcReference(schemaId = "ProviderController", microserviceName = "provider") private ProviderService providerService; // consumer service which delegate the implementation to provider service. @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } @GetMapping("/getConfig") public String getConfig(@RequestParam("key") String key) { return providerService.getConfig(key); } @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public User testContentType(@RequestBody User user) { return providerService.testContentType(user); } } ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") private IHeaderParamWithListSchemaSpringMvc provider; @Override public String headerListDefault(List headerList) { return provider.headerListDefault(headerList); } @Override public String headerListCSV(List headerList) { return provider.headerListCSV(headerList); } @Override public String headerListMULTI(List headerList) { return provider.headerListMULTI(headerList); } @Override public String headerListSSV(List headerList) { return provider.headerListSSV(headerList); } @Override public String headerListPIPES(List headerList) { return provider.headerListPIPES(headerList); } } ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") public class ConsumerReactiveStreamController { interface ProviderReactiveStreamController { Publisher sseString(); Publisher sseModel(); } @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") ProviderReactiveStreamController controller; public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") @Transport(name = CoreConst.RESTFUL) public Publisher sseString() { return controller.sseString(); } @GetMapping("/sseModel") @Transport(name = CoreConst.RESTFUL) public Publisher sseModel() { return controller.sseModel(); } } ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class EtcdConsumerApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdConsumerApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface ProviderService { String sayHello(String name); String getConfig(String key); User testContentType(User user); } ================================================ FILE: demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-etcd/consumer/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-etcd version: 0.0.1 name: consumer properties: group: red registry: etcd: connectString: http://127.0.0.1:2379 config: etcd: connectString: http://127.0.0.1:2379 rest: address: 0.0.0.0:9092?websocketEnabled=true server: websocket-prefix: /ws ================================================ FILE: demo/demo-etcd/consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-etcd/gateway/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-etcd 3.4.0-SNAPSHOT etcd-gateway Java Chassis::Demo::Etcd::GATEWAY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb edge-core com.google.protobuf protobuf-java 3.25.5 runtime org.apache.servicecomb registry-etcd org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-etcd/gateway/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-etcd version: 0.0.1 name: gateway registry: etcd: enabled: true connectString: http://127.0.0.1:2379 rest: address: 0.0.0.0:9090?websocketEnabled=true server: websocket-prefix: /ws http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: consumer: prefixSegmentCount: 0 path: "/.*" microserviceName: consumer versionRule: 0.0.0+ websocket: mappings: consumer: prefixSegmentCount: 0 path: "/ws/.*" microserviceName: consumer versionRule: 0.0.0+ ================================================ FILE: demo/demo-etcd/gateway/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-etcd/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-etcd Java Chassis::Demo::Etcd pom org.apache.servicecomb.demo demo-schema org.apache.servicecomb solution-basic org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-core org.apache.logging.log4j log4j-api provider consumer gateway test-client ================================================ FILE: demo/demo-etcd/provider/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-etcd 3.4.0-SNAPSHOT etcd-provider Java Chassis::Demo::Etcd::PROVIDER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone com.google.protobuf protobuf-java 3.25.5 runtime org.apache.servicecomb registry-etcd org.apache.servicecomb config-etcd io.reactivex.rxjava3 rxjava org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class EtcdProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { @Override public String headerListDefault(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListCSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListMULTI(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListSSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListPIPES(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } } ================================================ FILE: demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController implements InitializingBean { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { // return "Hello " + environment.getProperty("servicecomb.rest.address"); return "Hello " + name; } @GetMapping("/getConfig") public String getConfig(@RequestParam("key") String key) { return environment.getProperty(key); } @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public User testContentType(@RequestBody User user) { return user; } @Override public void afterPropertiesSet() throws Exception { } } ================================================ FILE: demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.reactivex.rxjava3.core.Flowable; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") @Transport(name = CoreConst.RESTFUL) public class ReactiveStreamController { public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") public Publisher sseString() { return Flowable.fromArray("a", "b", "c"); } @GetMapping("/sseModel") public Publisher sseModel() { return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) .map(item -> new Model("jack", item.intValue())); } } ================================================ FILE: demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.ServerWebSocket; @RestSchema(schemaId = "WebsocketController") @RequestMapping(path = "/ws") public class WebsocketController { @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) public void websocket(ServerWebSocket serverWebsocket) { // Client may have not registered message handler, and messages sent may get lost. // So we sleep for a while to send message. AtomicInteger receiveCount = new AtomicInteger(0); serverWebsocket.textMessageHandler(s -> { receiveCount.getAndIncrement(); }); serverWebsocket.closeHandler((v) -> System.out.println("closed")); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } serverWebsocket.writeTextMessage("hello"); for (int i = 0; i < 5; i++) { serverWebsocket.writeTextMessage("hello " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } serverWebsocket.writeTextMessage("total " + receiveCount.get()); serverWebsocket.close(); }).start(); } } ================================================ FILE: demo/demo-etcd/provider/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations servicecomb: service: application: demo-etcd version: 0.0.1 name: provider properties: group: green registry: etcd: connectString: http://127.0.0.1:2379 config: etcd: connectString: http://127.0.0.1:2379 instance-tag: tag1 rest: address: 0.0.0.0:9094?websocketEnabled=true server: websocket-prefix: /ws cors: enabled: true origin: "*" allowCredentials: false allowedMethod: "*" maxAge: 3600 key1: 1 key2: 3 key3: 5 test1: env test2: applition test3: service test4: version test5: tag ================================================ FILE: demo/demo-etcd/provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-etcd/test-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-etcd 3.4.0-SNAPSHOT etcd-test-client Java Chassis::Demo::Etcd::TEST-CLIENT jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-local io.etcd jetcd-core com.google.protobuf protobuf-java 3.25.5 runtime docker io.fabric8 docker-maven-plugin bitnami/etcd:3.5.16 etcd alias etcdserver 2379 2379:2379 yes etcd-provider:${project.version} etcd-provider alias -Dservicecomb.registry.etcd.connectString=http://etcd:2379 -Dservicecomb.config.etcd.connectString=http://etcd:2379 /maven/maven/etcd-provider-${project.version}.jar etcd:etcd ServiceComb is ready 9094 9094:9094 etcd-consumer:${project.version} etcd-consumer alias -Dservicecomb.registry.etcd.connectString=http://etcd:2379 -Dservicecomb.config.etcd.connectString=http://etcd:2379 /maven/maven/etcd-consumer-${project.version}.jar etcd:etcd ServiceComb is ready 9092 9092:9092 etcd-gateway:${project.version} etcd-gateway alias -Dservicecomb.registry.etcd.connectString=http://etcd:2379 /maven/maven/etcd-gateway-${project.version}.jar etcd:etcd ServiceComb is ready 9090 9090:9090 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface Config { String GATEWAY_URL = "http://localhost:9090"; } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.ConditionWaiter.SleepUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; @Component public class EtcdConfigIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigIT.class); @Override public void testRestTransport() throws Exception { testEnvironment(); testApplication(); testService(); testVersion(); testTag(); testOverride(); } private void testOverride() { putValue("/servicecomb/config/environment/production/application2.properties", "testValue=t1"); putValue("/servicecomb/config/application/production/demo-etcd/application2.properties", "testValue=t2"); testGetConfig("testValue", "t2"); putValue("/servicecomb/config/service/production/demo-etcd/provider/application2.properties", "testValue=t3"); testGetConfig("testValue", "t3"); putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application2.properties", "testValue=t4"); testGetConfig("testValue", "t4"); putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application2.properties", "testValue=t5"); testGetConfig("testValue", "t5"); } private void testEnvironment() { putValue("/servicecomb/config/environment/production/application.properties", "test1=env"); putValue("/servicecomb/config/environment/production/application.properties", "test1=env1"); testGetConfig("test1", "env1"); } private void testApplication() { putValue("/servicecomb/config/application/production/demo-etcd/application.properties", "test2=applition"); putValue("/servicecomb/config/application/production/demo-etcd/application.properties", "test2=applition2"); testGetConfig("test2", "applition2"); } private void testService() { putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", "test3=service"); putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", "test3=service3"); testGetConfig("test3", "service3"); } private void testVersion() { putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", "test3=version"); putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", "test4=version4"); testGetConfig("test4", "version4"); } private void testTag() { putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", "test5=tag"); putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", "test5=tag5"); testGetConfig("test5", "tag5"); } public void putValue(String key, String value) { try (Client client = Client.builder().endpoints("http://localhost:2379").build()) { client.getKVClient().put( ByteSequence.from(key, StandardCharsets.UTF_8), ByteSequence.from(value, StandardCharsets.UTF_8) ).get(); LOGGER.info("Value set successfully:{}", value); } catch (Exception e) { e.printStackTrace(); } } private void testGetConfig(String key, String expectValue) { String result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); for (int i = 0; i < 4; i++) { if (StringUtils.equals(expectValue, result)) { TestMgr.check(expectValue, result); break; } SleepUtil.sleep(500, TimeUnit.MILLISECONDS); } } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HeaderParamWithListSchemaIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHeaderListDefault(); testHeaderListMulti(); testHeaderListCSV(); testHeaderListSSV(); testHeaderListPipes(); } // default to multi private void testHeaderListDefault() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListPipes() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a|b|c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListSSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a b c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListCSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a,b,c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); headers.add("headerList", "a, b, c"); entity = new HttpEntity<>(headers); result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListMulti() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHelloWorld(); testGetConfig(); } private void testGetConfig() { String result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); TestMgr.check("1", result); result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); TestMgr.check("3", result); result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); TestMgr.check("5", result); } private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); TestMgr.check("Hello World", result); // test trace id added MultiValueMap headers = new HttpHeaders(); headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); HttpEntity entity = new HttpEntity(headers); ResponseEntity response = template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); TestMgr.check("Hello World", response.getBody()); } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class ProviderIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); private static final Logger LOGGER = LoggerFactory.getLogger(ProviderIT.class); @Override public void testRestTransport() throws Exception { User user = getUser("Application/json"); TestMgr.check(1L, user.getId()); TestMgr.check("czd", user.getName()); User user2 = getUser("application/json"); TestMgr.check(1L, user2.getId()); TestMgr.check("czd", user2.getName()); User user3 = getUser("APPLICATION/JSON"); TestMgr.check(1L, user3.getId()); TestMgr.check("czd", user3.getName()); } private User getUser(String contentType) throws IOException { HttpHeaders headers = new HttpHeaders(); headers.set("Content-Type", contentType); String requestBody = """ { "id": 1, "name": "czd" } """; HttpEntity entity = new HttpEntity<>(requestBody, headers); String url = Config.GATEWAY_URL + "/testContentType"; ResponseEntity response = template.exchange( url, HttpMethod.POST, entity, String.class); User user = JsonUtils.readValue(response.getBody().getBytes(StandardCharsets.UTF_8), User.class); return user; } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class ReactiveStreamIT implements CategorizedTestCase { @Autowired @Qualifier("reactiveStreamProvider") ReactiveStreamClient reactiveStreamProvider; @Autowired @Qualifier("reactiveStreamGateway") ReactiveStreamClient reactiveStreamGateway; @Override public void testRestTransport() throws Exception { testSseString(reactiveStreamProvider); testSseModel(reactiveStreamProvider); testSseString(reactiveStreamGateway); testSseModel(reactiveStreamGateway); } private void testSseModel(ReactiveStreamClient client) throws Exception { Publisher result = client.sseModel(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(Model s) { buffer.append(s.getName()).append(s.getAge()); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); } private void testSseString(ReactiveStreamClient client) throws Exception { Publisher result = client.sseString(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(String s) { buffer.append(s); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("abc", buffer.toString()); } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.apache.servicecomb.provider.pojo.Invoker; import org.reactivestreams.Publisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.WebSocket; @Configuration public class ThirdSvcConfiguration { @RequestMapping(path = "/ws") public interface WebsocketClient { @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) WebSocket websocket(); } @RequestMapping(path = "/") public interface ReactiveStreamClient { class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") Publisher sseString(); @GetMapping("/sseModel") Publisher sseModel(); } @Bean public RegistryBean providerServiceBean() { return new RegistryBean() .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) .setAppId("demo-etcd") .setServiceName("provider") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of( new Instance().setEndpoints(List.of("rest://localhost:9094"))))); } @Bean public RegistryBean gatewayServiceBean() { return new RegistryBean() .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) .addSchemaInterface("WebsocketController", WebsocketClient.class) .setAppId("demo-etcd") .setServiceName("gateway") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of( new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); } @Bean("reactiveStreamProvider") public ReactiveStreamClient reactiveStreamProvider() { return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); } @Bean("reactiveStreamGateway") public ReactiveStreamClient reactiveStreamGateway() { return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); } @Bean public WebsocketClient gatewayWebsocketClient() { return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import io.vertx.core.http.WebSocket; @Component public class WebsocketIT implements CategorizedTestCase { @Autowired private WebsocketClient websocketClient; @Override public void testRestTransport() throws Exception { StringBuffer sb = new StringBuffer(); AtomicBoolean closed = new AtomicBoolean(false); CountDownLatch latch = new CountDownLatch(1); WebSocket webSocket = websocketClient.websocket(); webSocket.textMessageHandler(s -> { sb.append(s); sb.append(" "); webSocket.writeTextMessage(s); }); webSocket.closeHandler(v -> { closed.set(true); latch.countDown(); }); latch.await(30, TimeUnit.SECONDS); TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); TestMgr.check(closed.get(), true); } } ================================================ FILE: demo/demo-etcd/test-client/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-etcd name: test-client version: 0.0.1 rest: address: 0.0.0.0:9097 # should be same with server.port to use web container config: etcd: instance-tag: tag1 test1: env test2: applition test3: service test4: version test5: tag ================================================ FILE: demo/demo-etcd/test-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = TestClientApplication.class) public class EtcdIT { private static final Logger LOGGER = LoggerFactory.getLogger(EtcdIT.class); @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() { try { TestClientApplication.run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-filter/filter-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-filter 3.4.0-SNAPSHOT filter-client Java Chassis::Demo::Filter::Client org.apache.servicecomb.demo demo-schema org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb.demo.filter.FilterClient org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class FilterClient { private static final Logger LOGGER = LoggerFactory.getLogger(FilterClient.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder(FilterClient.class).web(WebApplicationType.NONE).run(args); run(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("com.servicecomb.filterServer"); } } ================================================ FILE: demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.client; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestOperations; @RestSchema(schemaId = "ClientExceptionSchema") @RequestMapping(path = "/exception", produces = MediaType.APPLICATION_JSON_VALUE) public class ClientExceptionSchema { interface IExceptionSchema { boolean blockingException(); CompletableFuture reactiveException(); } private IExceptionSchema exceptionSchema; private RestOperations restTemplate = RestTemplateBuilder.create(); @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "ExceptionSchema") public void setExceptionSchema(IExceptionSchema exceptionSchema) { this.exceptionSchema = exceptionSchema; } @GetMapping(path = "/blockingExceptionRestTemplate") public boolean blockingExceptionRestTemplate() { return restTemplate.getForObject( "servicecomb://com.servicecomb.filterServer/exception/blockingException", boolean.class); } @GetMapping(path = "/blockingExceptionReference") public boolean blockingExceptionReference() { return exceptionSchema.blockingException(); } @GetMapping(path = "/blockingExceptionInvoker") public boolean blockingExceptionInvoker() { return InvokerUtils.syncInvoke("com.servicecomb.filterServer", "ExceptionSchema", "blockingException", null, boolean.class); } @GetMapping(path = "/reactiveExceptionReference") public CompletableFuture reactiveExceptionReference() { return exceptionSchema.reactiveException(); } } ================================================ FILE: demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.client; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "GovernanceConsumerSchema") @RequestMapping(path = "/govern", produces = MediaType.APPLICATION_JSON_VALUE) public class GovernanceConsumerSchema { interface GovernanceProviderSchemaInf { boolean providerFlowControl(); } @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "GovernanceProviderSchema") private GovernanceProviderSchemaInf retrySchemaInf; @GetMapping(path = "/edgeFlowControl") public boolean edgeFlowControl() { return true; } @GetMapping(path = "/providerFlowControl") public boolean providerFlowControl() { return retrySchemaInf.providerFlowControl(); } } ================================================ FILE: demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.client; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "RetryClientSchema") @RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) public class RetryClientSchema { interface RetrySchemaInf { CompletableFuture successWhenRetryAsync(); } @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "RetrySchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); private AtomicLong counter = new AtomicLong(0); @GetMapping(path = "/governance/edgeSuccessWhenRetry") public boolean edgeSuccessWhenRetry() { if (counter.getAndIncrement() % 3 != 0) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); } return true; } @GetMapping(path = "/governance/edgeSuccessWhenRetryAsync") public CompletableFuture edgeSuccessWhenRetryAsync() { CompletableFuture result = new CompletableFuture<>(); if (counter.getAndIncrement() % 2 == 0) { result.completeExceptionally(new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later.")); } else { result.complete(true); } return result; } @GetMapping(path = "/governance/successWhenRetry") public boolean successWhenRetry() { return restTemplate.getForObject("servicecomb://com.servicecomb.filterServer/retry/governance/successWhenRetry", Boolean.class); } @GetMapping(path = "/governance/successWhenRetryAsync") public CompletableFuture successWhenRetryAsync() { return retrySchemaInf.successWhenRetryAsync(); } } ================================================ FILE: demo/demo-filter/filter-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-filter/filter-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: filtertest # test service name with dot name: com.servicecomb.filterClient version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8082 server: compression: true highway: address: 0.0.0.0:7072 invocation: exception: print-stack-trace: true # test governance retry matchGroup: retry-governance: | matches: - apiPath: prefix: "/retry/governance/" retry: retry-governance: | maxAttempts: 2 retryOnResponseStatus: [500] ================================================ FILE: demo/demo-filter/filter-edge/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-filter 3.4.0-SNAPSHOT filter-edge Java Chassis::Demo::Filter::Edge org.apache.servicecomb.demo demo-schema org.apache.servicecomb edge-core org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb.demo.filter.FilterEdge org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-filter/filter-edge/src/main/java/org/apache/servicecomb/demo/filter/FilterEdge.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class FilterEdge { private static final Logger LOGGER = LoggerFactory.getLogger(FilterEdge.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder(FilterEdge.class).web(WebApplicationType.NONE).run(args); run(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("filterEdge"); } } ================================================ FILE: demo/demo-filter/filter-edge/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-filter/filter-edge/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: filtertest # test service name with dot name: com.servicecomb.filterEdge version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:9090 server: compression: true edge: filter: addHeader: allowedHeaders: X-B3-TraceId invocation: exception: print-stack-trace: true http: dispatcher: edge: default: enabled: true prefix: service withVersion: false prefixSegmentCount: 2 url: enabled: true mappings: filterClient: prefixSegmentCount: 0 path: "/retry/.*" microserviceName: com.servicecomb.filterClient filterClient2: prefixSegmentCount: 0 path: "/govern/.*" microserviceName: com.servicecomb.filterClient matchGroup: edgeFlowControl: | matches: - apiPath: exact: "/govern/edgeFlowControl" retry-governance: | matches: - apiPath: prefix: "/retry/governance/" rateLimiting: edgeFlowControl: | timeoutDuration: 0 limitRefreshPeriod: 1000 rate: 1 retry: retry-governance: | maxAttempts: 2 retryOnResponseStatus: [500] ================================================ FILE: demo/demo-filter/filter-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-filter 3.4.0-SNAPSHOT filter-server Java Chassis::Demo::Filter::Server org.apache.servicecomb.demo demo-schema org.apache.servicecomb.demo.filter.FilterServer org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/FilterServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class FilterServer { private static final Logger LOGGER = LoggerFactory.getLogger(FilterServer.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder(FilterServer.class).web(WebApplicationType.NONE).run(args); run(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("filterServer"); } } ================================================ FILE: demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/ExceptionSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.server; import java.util.concurrent.CompletableFuture; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "ExceptionSchema") @RequestMapping(path = "/exception", produces = MediaType.APPLICATION_JSON_VALUE) public class ExceptionSchema { @GetMapping(path = "/blockingException") public boolean blockingException() { throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("Blocking Exception")); } @GetMapping(path = "/reactiveException") public CompletableFuture reactiveException() { throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("Reactive Exception")); } } ================================================ FILE: demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "GovernanceProviderSchema") @RequestMapping(path = "/govern", produces = MediaType.APPLICATION_JSON_VALUE) public class GovernanceProviderSchema { @GetMapping(path = "/providerFlowControl") public boolean providerFlowControl() { return true; } } ================================================ FILE: demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/RetrySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.server; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; // test cases for retry @RestSchema(schemaId = "RetrySchema") @RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) public class RetrySchema { private AtomicLong counter = new AtomicLong(0); @GetMapping(path = "/governance/successWhenRetry") public boolean successWhenRetry() { if (counter.getAndIncrement() % 3 != 0) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); } return true; } @GetMapping(path = "/governance/successWhenRetryAsync") public CompletableFuture successWhenRetryAsync() { CompletableFuture result = new CompletableFuture<>(); if (counter.getAndIncrement() % 2 == 0) { result.completeExceptionally(new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later.")); } else { result.complete(true); } return result; } } ================================================ FILE: demo/demo-filter/filter-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-filter/filter-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: filtertest # test service name with dot name: com.servicecomb.filterServer version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 server: compression: true highway: address: 0.0.0.0:7070 matchGroup: providerFlowControl: | matches: - apiPath: exact: "/govern/providerFlowControl" rateLimiting: providerFlowControl: | timeoutDuration: 0 limitRefreshPeriod: 1000 rate: 1 #########SSL options ssl.protocols: TLSv1.2 ssl.authPeer: true ssl.checkCN.host: true #########certificates config ssl.trustStore: trust.jks ssl.trustStoreType: JKS ssl.trustStoreValue: Changeme_123 ssl.keyStore: server.p12 ssl.keyStoreType: PKCS12 ssl.keyStoreValue: Changeme_123 ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom vertx.disableFileCPResolving: false # false: create the .vertx directory, true: do not create ================================================ FILE: demo/demo-filter/filter-tests/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-filter 3.4.0-SNAPSHOT filter-tests Java Chassis::Demo::Filter::Tests org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-local org.apache.servicecomb.demo.filter.FilterTests docker io.fabric8 docker-maven-plugin servicecomb/service-center service30100 alias server is ready 30100 30100:30100 filter-server:${project.version} filter-server alias -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/filter-server-${project.version}.jar service30100:service30100 ServiceComb is ready 8080 8080:8080 filter-client:${project.version} filter-client alias -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/filter-client-${project.version}.jar service30100:service30100 ServiceComb is ready 8082 8082:8082 filter-edge:${project.version} filter-edge alias -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/filter-edge-${project.version}.jar service30100:service30100 ServiceComb is ready 9090 9090:9090 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/FilterTests.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class FilterTests { private static final Logger LOGGER = LoggerFactory.getLogger(FilterTests.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder(FilterTests.class).web(WebApplicationType.NONE).run(args); run(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("filterTests"); } } ================================================ FILE: demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.tests; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestExceptionSchemaFromClient implements CategorizedTestCase { RestOperations restTemplate = RestTemplateBuilder.create(); private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; @Override public String getMicroserviceName() { return "filterEdge"; } @Override public void testAllTransport() throws Exception { testBlockingExceptionRestTemplate(); testBlockingExceptionReference(); testBlockingExceptionInvoker(); testReactiveExceptionReference(); } private void testBlockingExceptionRestTemplate() { try { restTemplate.getForObject(SERVER + "/exception/blockingExceptionRestTemplate", boolean.class); } catch (InvocationException e) { TestMgr.check(503, e.getStatus().getStatusCode()); TestMgr.check("Blocking Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); } } private void testBlockingExceptionReference() { try { restTemplate.getForObject(SERVER + "/exception/blockingExceptionReference", boolean.class); } catch (InvocationException e) { TestMgr.check(503, e.getStatus().getStatusCode()); TestMgr.check("Blocking Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); } } private void testBlockingExceptionInvoker() { try { restTemplate.getForObject(SERVER + "/exception/blockingExceptionInvoker", boolean.class); } catch (InvocationException e) { TestMgr.check(503, e.getStatus().getStatusCode()); TestMgr.check("Blocking Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); } } private void testReactiveExceptionReference() { try { restTemplate.getForObject(SERVER + "/exception/reactiveExceptionReference", boolean.class); } catch (InvocationException e) { TestMgr.check(503, e.getStatus().getStatusCode()); TestMgr.check("Reactive Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); } } } ================================================ FILE: demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.tests; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestGovernanceSchemaFromEdge implements CategorizedTestCase { interface GovernanceEdgeSchemaInf { boolean edgeFlowControl(); boolean providerFlowControl(); } @RpcReference(microserviceName = "com.servicecomb.filterEdge", schemaId = "GovernanceConsumerSchema") private GovernanceEdgeSchemaInf retrySchemaInf; @Override public String getMicroserviceName() { return "com.servicecomb.filterEdge"; } @Override public void testRestTransport() throws Exception { testEdgeFlowControl(); testConsumerFlowControl(); } private void testConsumerFlowControl() throws Exception { CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { boolean result = retrySchemaInf.providerFlowControl(); if (!result) { notExpectedFailed.set(true); } } catch (Exception e) { if (!e.getMessage().contains("rate limited")) { notExpectedFailed.set(true); } expectedFailed.set(true); } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(expectedFailed.get(), true); TestMgr.check(notExpectedFailed.get(), false); } private void testEdgeFlowControl() throws Exception { CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { boolean result = retrySchemaInf.edgeFlowControl(); if (!result) { notExpectedFailed.set(true); } } catch (Exception e) { if (!e.getMessage().contains("rate limited")) { notExpectedFailed.set(true); } expectedFailed.set(true); } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(expectedFailed.get(), true); TestMgr.check(notExpectedFailed.get(), false); } } ================================================ FILE: demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.tests; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestRetrySchemaFromClient implements CategorizedTestCase { interface RetrySchemaInf { boolean successWhenRetry(); CompletableFuture successWhenRetryAsync(); } @RpcReference(microserviceName = "com.servicecomb.filterClient", schemaId = "RetryClientSchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; @Override public String getMicroserviceName() { return "com.servicecomb.filterClient"; } @Override public void testAllTransport() throws Exception { testRetryGovernanceRestTemplate(); testRetryGovernanceRpc(); } private void testRetryGovernanceRpc() throws Exception { TestMgr.check(retrySchemaInf.successWhenRetry(), true); TestMgr.check(retrySchemaInf.successWhenRetry(), true); TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); } private void testRetryGovernanceRestTemplate() { TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); } } ================================================ FILE: demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.tests; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class TestRetrySchemaFromEdge implements CategorizedTestCase { interface RetrySchemaInf { boolean edgeSuccessWhenRetry(); boolean successWhenRetry(); CompletableFuture edgeSuccessWhenRetryAsync(); CompletableFuture successWhenRetryAsync(); } @RpcReference(microserviceName = "com.servicecomb.filterEdge", schemaId = "RetryClientSchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); RestTemplate springRestTemplate = new RestTemplate(); private static final String SERVER = "servicecomb://com.servicecomb.filterEdge"; private static final String EDGE_SERVER = "http://127.0.0.1:9090"; @Override public String getMicroserviceName() { return "com.servicecomb.filterEdge"; } @Override public void testRestTransport() throws Exception { testRetryGovernanceFromEdgeDefaultDispatcher(); testRetryGovernanceRestTemplate(); testRetryGovernanceRpc(); testEdgeRetryGovernanceRpc(); } private void testRetryGovernanceRpc() throws Exception { TestMgr.check(retrySchemaInf.successWhenRetry(), true); TestMgr.check(retrySchemaInf.successWhenRetry(), true); TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); } private void testEdgeRetryGovernanceRpc() throws Exception { TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true); TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true); TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true); TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true); } private void testRetryGovernanceRestTemplate() { TestMgr.check(restTemplate.getForObject( SERVER + "/retry/governance/successWhenRetry", boolean.class), true); TestMgr.check(restTemplate.getForObject( SERVER + "/retry/governance/successWhenRetry", boolean.class), true); } private void testRetryGovernanceFromEdgeDefaultDispatcher() { TestMgr.check(springRestTemplate.getForObject( EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); TestMgr.check(springRestTemplate.getForObject( EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); } } ================================================ FILE: demo/demo-filter/filter-tests/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-filter/filter-tests/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: filtertest # test service name with dot name: com.servicecomb.filterTests version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 invocation: exception: print-stack-trace: true ================================================ FILE: demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.filter.client.GovernanceConsumerSchema version: 1.0.0 servers: - url: /govern paths: /edgeFlowControl: get: operationId: edgeFlowControl responses: "200": description: response of 200 content: application/json: schema: type: boolean /providerFlowControl: get: operationId: providerFlowControl responses: "200": description: response of 200 content: application/json: schema: type: boolean components: {} ================================================ FILE: demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.filter.client.RetryClientSchema version: 1.0.0 servers: - url: /retry paths: /governance/edgeSuccessWhenRetry: get: operationId: edgeSuccessWhenRetry responses: "200": description: response of 200 content: application/json: schema: type: boolean /governance/edgeSuccessWhenRetryAsync: get: operationId: edgeSuccessWhenRetryAsync responses: "200": description: response of 200 content: application/json: schema: type: boolean /governance/successWhenRetry: get: operationId: successWhenRetry responses: "200": description: response of 200 content: application/json: schema: type: boolean /governance/successWhenRetryAsync: get: operationId: successWhenRetryAsync responses: "200": description: response of 200 content: application/json: schema: type: boolean components: {} ================================================ FILE: demo/demo-filter/filter-tests/src/main/resources/registry.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- com.servicecomb.filterEdge: - id: "001" version: "1.0" appid: filtertest instances: - endpoints: - rest://127.0.0.1:9090 ================================================ FILE: demo/demo-filter/filter-tests/src/test/java/org/apache/servicecomb/demo/filter/retry/FilterTestsIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.filter.retry; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.filter.FilterTests; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = FilterTests.class) public class FilterTestsIT { @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { FilterTests.run(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-filter/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-filter Java Chassis::Demo::Filter pom filter-server filter-client filter-edge filter-tests org.apache.servicecomb solution-basic org.apache.servicecomb registry-service-center org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-jaxrs/jaxrs-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-jaxrs 3.4.0-SNAPSHOT jaxrs-client Java Chassis::Demo::JAXRS::Client org.apache.servicecomb.demo demo-schema org.apache.servicecomb.demo.jaxrs.JaxrsClient docker jaxrs-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpStatus; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.CodeFirstRestTemplate; import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.RestObjectMapperWithStringMapper; import org.apache.servicecomb.demo.RestObjectMapperWithStringMapperNotWriteNull; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.jaxrs.client.CodeFirstRestTemplateJaxrs; import org.apache.servicecomb.demo.jaxrs.client.pojoDefault.DefaultModelServiceClient; import org.apache.servicecomb.demo.jaxrs.client.validation.ValidationServiceClient; import org.apache.servicecomb.demo.validator.Student; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; @SpringBootApplication public class JaxrsClient { private static final Logger LOGGER = LoggerFactory.getLogger(JaxrsClient.class); private static RestOperations templateNew = RestTemplateBuilder.create(); public static void main(String[] args) throws Exception { new SpringApplicationBuilder(JaxrsClient.class).web(WebApplicationType.NONE).run(args); init(); try { run(); } catch (Exception e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); LOGGER.info("-------------- last time updated checks(maybe more/less): 540 -------------"); } public static void init() throws Exception { RestObjectMapperFactory.setDefaultRestObjectMapper(new RestObjectMapperWithStringMapper()); RestObjectMapperFactory.setConsumerWriterMapper(new RestObjectMapperWithStringMapperNotWriteNull()); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("jaxrs"); CodeFirstRestTemplate codeFirstClient = BeanUtils.getBean(CodeFirstRestTemplateJaxrs.class); codeFirstClient.testCodeFirst(templateNew, "jaxrs", "/codeFirstJaxrs/"); testCompute(templateNew); testValidator(templateNew); testJaxRSDefaultValuesAllTransport(templateNew); testSpringMvcDefaultValuesJavaPrimitiveAllTransport(templateNew); DefaultModelServiceClient.run(); ValidationServiceClient.run(); testOnlyRest(templateNew); } private static void testOnlyRest(RestOperations template) { String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName; InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, "rest"); testGetRest(template, cseUrlPrefix); testSpringMvcDefaultValuesJavaPrimitiveRest(templateNew); } private static void testCompute(RestOperations template) throws Exception { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName; testGetAllTransport(template, cseUrlPrefix); testPost(template, cseUrlPrefix); testPut(template, cseUrlPrefix); testDelete(template, cseUrlPrefix); testExchange(template, cseUrlPrefix); testRawJsonParam(template, cseUrlPrefix); } } private static void testValidator(RestOperations template) { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName + "/validator/"; testValidatorAddSuccess(template, cseUrlPrefix); if ("rest".equals(transport)) { testValidatorAddFail(template, cseUrlPrefix); testValidatorSayHiFail(template, cseUrlPrefix); testValidatorExchangeFail(template, cseUrlPrefix); testJaxRSDefaultValuesRest(template); } else if ("highway".equals(transport)) { testValidatorAddFail(template, cseUrlPrefix); testValidatorSayHiFail(template, cseUrlPrefix); testValidatorExchangeFail(template, cseUrlPrefix); } testValidatorSayHiSuccess(template, cseUrlPrefix); testValidatorExchangeSuccess(template, cseUrlPrefix); } } private static void testJaxRSDefaultValuesRest(RestOperations template) { String result; String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; boolean failed = false; try { result = template.getForObject(cseUrlPrefix + "/query2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } failed = false; try { result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(failed, true); failed = false; try { result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(failed, true); result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); TestMgr.check("Hello 20bobo40302", result); failed = false; try { result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(failed, true); } private static void testJaxRSDefaultValuesAllTransport(RestOperations template) { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; //default values HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); HttpEntity> request = new HttpEntity<>(map, headers); String result = template.postForObject(cseUrlPrefix + "/form", request, String.class); // TODO: do not support form parameters default value // TestMgr.check("Hello 20bobo", result); TestMgr.check("Hello 0null", result); headers = new HttpHeaders(); HttpEntity entity = new HttpEntity<>(null, headers); result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); TestMgr.check("Hello 20bobo30", result); result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); TestMgr.check("Hello 20bobo4010", result); boolean failed = false; try { result = template.getForObject(cseUrlPrefix + "/query2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } failed = false; try { result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(failed, true); failed = false; try { result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(failed, true); result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); TestMgr.check("Hello 20bobo40302", result); failed = false; try { result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(failed, true); result = template.getForObject(cseUrlPrefix + "/query3?a=30&b=2", String.class); TestMgr.check("Hello 302", result); result = template.getForObject(cseUrlPrefix + "/query3?a=30", String.class); TestMgr.check("Hello 30null", result); //input values headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); Map params = new HashMap<>(); params.put("a", "30"); params.put("b", "sam"); HttpEntity> requestPara = new HttpEntity<>(params, headers); result = template.postForObject(cseUrlPrefix + "/form", requestPara, String.class); TestMgr.check("Hello 30sam", result); headers = new HttpHeaders(); headers.add("a", "30"); headers.add("b", "sam"); headers.add("c", "40"); entity = new HttpEntity<>(null, headers); result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); TestMgr.check("Hello 30sam40", result); result = template.getForObject(cseUrlPrefix + "/query?a=3&b=sam&c=5&d=30", String.class); TestMgr.check("Hello 3sam530", result); result = template.getForObject(cseUrlPrefix + "/query2?a=3&b=4&c=5&d=30&e=2", String.class); TestMgr.check("Hello 345302", result); } } private static void testGetRest(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); int result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, params); TestMgr.check(2, result); result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, 5, 4); TestMgr.check(1, result); result = template.getForObject(cseUrlPrefix + "/compute/reduce?a=5&b=6", Integer.class); TestMgr.check(-1, result); } private static void testGetAllTransport(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); int result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, params); TestMgr.check(2, result); result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, 5, 4); TestMgr.check(1, result); result = template.getForObject(cseUrlPrefix + "/compute/reduce?a=5&b=6", Integer.class); TestMgr.check(-1, result); } private static void testPost(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); int result = template.postForObject(cseUrlPrefix + "/compute/add", params, Integer.class); TestMgr.check(8, result); Person person = new Person(); person.setName("world"); Person resultPerson = template.postForObject(cseUrlPrefix + "/compute/sayhello", person, Person.class); TestMgr.check("hello world", resultPerson.getName()); HttpHeaders headers = new HttpHeaders(); headers.add("prefix", "haha"); HttpEntity reqEntity = new HttpEntity<>(person, headers); TestMgr.check("haha world", template.postForObject(cseUrlPrefix + "/compute/saysomething", reqEntity, String.class)); } private static void testPut(RestOperations template, String cseUrlPrefix) { template.put(cseUrlPrefix + "/compute/sayhi/{name}", null, "world"); } private static void testDelete(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("name", "world"); template.delete(cseUrlPrefix + "/compute/sayhei/?name={name}", params); } private static void testExchange(RestOperations template, String cseUrlPrefix) { HttpHeaders headers = new HttpHeaders(); headers.add("Accept", MediaType.APPLICATION_JSON); Person person = new Person(); person.setName("world"); HttpEntity requestEntity = new HttpEntity<>(person, headers); ResponseEntity resEntity = template.exchange(cseUrlPrefix + "/compute/sayhello", HttpMethod.POST, requestEntity, Person.class); TestMgr.check("hello world", resEntity.getBody()); ResponseEntity resEntity2 = template.exchange(cseUrlPrefix + "/compute/addstring?s=abc&s=def", HttpMethod.DELETE, null, String.class); TestMgr.check("abcdef", resEntity2.getBody()); } private static void testRawJsonParam(RestOperations template, String cseUrlPrefix) throws Exception { Map person = new HashMap<>(); person.put("name", "Tom"); String jsonPerson = RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(person); TestMgr.check("hello Tom", template.postForObject(cseUrlPrefix + "/compute/testrawjson", jsonPerson, String.class)); } @SuppressWarnings({"rawtypes"}) private static void testValidatorAddFail(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); boolean isExcep = false; try { template.postForObject(cseUrlPrefix + "add", params, Integer.class); } catch (InvocationException e) { isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); // Message depended on locale, so just check the short part. // 'must be greater than or equal to 20', propertyPath=add.arg1, rootBeanClass=class org.apache.servicecomb.demo.jaxrs.server.Validator, messageTemplate='{jakarta.validation.constraints.Min.message}'}]] // ignored if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); TestMgr.check("invalid parameters.", data.getMessage()); TestMgr.check("add.b", ((Map) ((List) data.getDynamic("validateDetail")).get(0)).get("propertyPath").toString()); } else { // rest decode/encode 'Object' using json without type information, got map. Users can got runtime type by adding @JsonTypeInfo to the model. Map data = (Map) e.getErrorData(); TestMgr.check("invalid parameters.", data.get("message").toString()); TestMgr.check("add.b", ((Map) ((List) data.get("validateDetail")).get(0)).get("propertyPath").toString()); } } TestMgr.check(true, isExcep); } private static void testValidatorAddSuccess(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "20"); int result = template.postForObject(cseUrlPrefix + "add", params, Integer.class); TestMgr.check(25, result); } @SuppressWarnings({"rawtypes"}) private static void testValidatorSayHiFail(RestOperations template, String cseUrlPrefix) { boolean isExcep = false; try { template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "te"); } catch (InvocationException e) { isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); // Message depended on locale, so just check the short part. if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); TestMgr.check("invalid parameters.", data.getMessage()); TestMgr.check("sayHi.name", ((Map) ((List) data.getDynamic("validateDetail")).get(0)).get("propertyPath").toString()); } else { Map data = (Map) e.getErrorData(); TestMgr.check("invalid parameters.", data.get("message").toString()); TestMgr.check("sayHi.name", ((Map) ((List) data.get("validateDetail")).get(0)).get("propertyPath").toString()); } } TestMgr.check(true, isExcep); } private static void testValidatorSayHiSuccess(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "world"); TestMgr.check(202, responseEntity.getStatusCode().value()); TestMgr.check("world sayhi", responseEntity.getBody()); } @SuppressWarnings({"rawtypes"}) private static void testValidatorExchangeFail(RestOperations template, String cseUrlPrefix) { HttpHeaders headers = new HttpHeaders(); headers.add("Accept", MediaType.APPLICATION_JSON); Student student = new Student(); student.setName(""); student.setAge(25); boolean isExcep = false; try { HttpEntity requestEntity = new HttpEntity<>(student, headers); template.exchange(cseUrlPrefix + "/sayhello", HttpMethod.POST, requestEntity, Student.class); } catch (InvocationException e) { isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); // Message depended on locale, so just check the short part. if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); TestMgr.check("invalid parameters.", data.getMessage()); TestMgr.check("sayHello.student.age", ((Map) ((List) data.getDynamic("validateDetail")).get(0)).get("propertyPath").toString()); } else { Map data = (Map) e.getErrorData(); TestMgr.check("invalid parameters.", data.get("message").toString()); TestMgr.check("sayHello.student.age", ((Map) ((List) data.get("validateDetail")).get(0)).get("propertyPath").toString()); } } TestMgr.check(true, isExcep); } private static void testValidatorExchangeSuccess(RestOperations template, String cseUrlPrefix) { Student student = new Student(); student.setName("test"); student.setAge(15); Student result = template.postForObject(cseUrlPrefix + "sayhello", student, Student.class); TestMgr.check("hello test 15", result); } private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestOperations template) { String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); HttpEntity> request = new HttpEntity<>(map, headers); //default values with primitive String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", request, String.class); // TODO: form default values support // TestMgr.check("Hello 0bobo", result); TestMgr.check("Hello 0null", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", request, String.class); TestMgr.check("Hello 0.0false", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", request, String.class); TestMgr.check("Hello", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", request, String.class); TestMgr.check("Hello nullnull", result); result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); } private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestOperations template) { String microserviceName = "jaxrs"; for (String transport : DemoConst.transports) { CategorizedTestCaseRunner.changeTransport(microserviceName, transport); String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); HttpEntity> request = new HttpEntity<>(map, headers); //default values with primitive String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", request, String.class); // TODO: form default values support // TestMgr.check("Hello 0bobo", result); TestMgr.check("Hello 0null", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", request, String.class); TestMgr.check("Hello 0.0false", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", request, String.class); TestMgr.check("Hello", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", request, String.class); TestMgr.check("Hello nullnull", result); result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import org.apache.servicecomb.demo.CodeFirstRestTemplate; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestOperations; @Component public class CodeFirstRestTemplateJaxrs extends CodeFirstRestTemplate { @Override protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { testDefaultPath(template, cseUrlPrefix); test404(template); super.testAllTransport(microserviceName, template, cseUrlPrefix); } private void testDefaultPath(RestOperations template, String cseUrlPrefix) { int result = template.getForObject(cseUrlPrefix.substring(0, cseUrlPrefix.length() - 1), Integer.class); TestMgr.check(100, result); } @Override protected void testOnlyRest(String microserviceName, RestOperations template, String cseUrlPrefix) { super.testOnlyRest(microserviceName, template, cseUrlPrefix); } private void test404(RestOperations template) { HttpClientErrorException exception = null; try { template.getForEntity("http://127.0.0.1:8080/aPathNotExist", String.class); TestMgr.check("expect throw but not", ""); } catch (RestClientException e) { if (e instanceof HttpClientErrorException) { exception = (HttpClientErrorException) e; } } TestMgr.check(404, exception.getStatusCode().value()); TestMgr.check(true, exception.getMessage().contains("404 Not Found")); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CustomLoadbalanceExtensionsFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import org.apache.servicecomb.loadbalance.Configuration; import org.apache.servicecomb.loadbalance.ExtensionsFactory; import org.apache.servicecomb.loadbalance.RoundRobinRuleExt; import org.apache.servicecomb.loadbalance.RuleExt; import org.springframework.stereotype.Component; @Component public class CustomLoadbalanceExtensionsFactory implements ExtensionsFactory { static class MyCustomRule extends RoundRobinRuleExt { } @Override public boolean isSupport(String key, String value) { return (Configuration.RULE_STRATEGY_NAME.equals(key) && "mycustomrule".equals(value)); } @Override public RuleExt createLoadBalancerRule(String ruleName) { return new MyCustomRule(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.multiErrorCode.MultiRequest; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse200; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse400; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse500; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import io.vertx.core.json.Json; import io.vertx.core.json.JsonObject; import jakarta.ws.rs.core.Response.Status; @Component public class MultiErrorCodeServiceClient implements CategorizedTestCase { private static final String SERVER = "cse://jaxrs"; private static RestOperations template = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { testErrorCode(); testErrorCodeWithHeader(); testErrorCodeWithHeaderJAXRS(); testErrorCodeWithHeaderJAXRSUsingRowType(); testNoClientErrorCode(); } @Override public void testRestTransport() throws Exception { testErrorCodeWrongType(); } @Override public void testHighwayTransport() throws Exception { } private static void testErrorCodeWrongType() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); String body = "{\"message\":\"hello\",\"code\":\"wrongType\""; HttpEntity entity = new HttpEntity<>(body, headers); try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); TestMgr.fail("expect failed."); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); } entity = new HttpEntity<>(null, headers); try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); TestMgr.fail("expect failed."); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); } // wrong type body = "{\"message\":\"hello\",\"code\":\"200\"}"; entity = new HttpEntity<>(body, headers); try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); TestMgr.fail("expect failed."); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); } } private static void testErrorCode() { MultiRequest request = new MultiRequest(); request.setCode(200); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse200.class); TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "success result"); request.setCode(400); MultiResponse400 t400 = null; try { template.postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse400.class); } catch (InvocationException e) { t400 = (MultiResponse400) e.getErrorData(); } TestMgr.check(t400.getCode(), 400); TestMgr.check(t400.getMessage(), "bad request"); request.setCode(500); MultiResponse500 t500 = null; try { template.postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse400.class); } catch (InvocationException e) { t500 = (MultiResponse500) e.getErrorData(); } TestMgr.check(t500.getCode(), 500); TestMgr.check(t500.getMessage(), "internal error"); } private static void testErrorCodeWithHeader() { MultiRequest request = new MultiRequest(); request.setCode(200); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse200.class); TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "success result"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); request.setCode(400); MultiResponse400 t400 = null; try { template.postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse400.class); } catch (InvocationException e) { t400 = (MultiResponse400) e.getErrorData(); TestMgr.check(e.getStatus().getStatusCode(), Status.BAD_REQUEST.getStatusCode()); } TestMgr.check(t400.getCode(), 400); TestMgr.check(t400.getMessage(), "bad request"); request.setCode(500); MultiResponse500 t500 = null; try { template.postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse400.class); } catch (InvocationException e) { t500 = (MultiResponse500) e.getErrorData(); TestMgr.check(e.getStatus().getStatusCode(), Status.INTERNAL_SERVER_ERROR.getStatusCode()); } TestMgr.check(t500.getCode(), 500); TestMgr.check(t500.getMessage(), "internal error"); } private static void testErrorCodeWithHeaderJAXRS() { MultiRequest request = new MultiRequest(); request.setCode(200); request.setMessage("success result"); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse200.class); TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "success result"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); request.setCode(400); request.setMessage("bad request"); MultiResponse400 t400 = null; try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse400.class); } catch (InvocationException e) { t400 = (MultiResponse400) e.getErrorData(); TestMgr.check(e.getStatus().getStatusCode(), Status.BAD_REQUEST.getStatusCode()); } TestMgr.check(t400.getCode(), 400); TestMgr.check(t400.getMessage(), "bad request"); request.setCode(500); request.setMessage("internal error"); MultiResponse500 t500 = null; try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse400.class); } catch (InvocationException e) { t500 = (MultiResponse500) e.getErrorData(); TestMgr.check(e.getStatus().getStatusCode(), Status.INTERNAL_SERVER_ERROR.getStatusCode()); } TestMgr.check(t500.getCode(), 500); TestMgr.check(t500.getMessage(), "internal error"); } private static void testErrorCodeWithHeaderJAXRSUsingRowType() { JsonObject requestJson = new JsonObject(); requestJson.put("code", 200); requestJson.put("message", "test message"); ResponseEntity result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", requestJson, MultiResponse200.class); TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "test message"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); MultiRequest request = new MultiRequest(); request.setCode(200); request.setMessage("test message"); String stringRequest = Json.encode(request); // wrap request to JsonObject result = template .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", new JsonObject(stringRequest), MultiResponse200.class); TestMgr.check(result.getStatusCode().value(), 200); TestMgr.check(result.getBody().getMessage(), "test message"); TestMgr.check(result.getBody().getCode(), 200); TestMgr.check(result.getHeaders().getFirst("x-code"), 200); } private static void testNoClientErrorCode() { JsonObject requestJson = new JsonObject(); requestJson.put("code", 200); requestJson.put("message", "test message"); @SuppressWarnings("rawtypes") ResponseEntity listResult = template .postForEntity(SERVER + "/MultiErrorCodeService/noClientErrorCode", requestJson, List.class); TestMgr.check(listResult.getStatusCode().value(), 200); Map mapResult = RestObjectMapperFactory.getRestObjectMapper().convertValue(listResult.getBody().get(0), Map.class); TestMgr.check(mapResult.get("message"), "test message"); TestMgr.check(mapResult.get("code"), 200); TestMgr.check(mapResult.get("t200"), 200); try { requestJson.put("code", 400); template .postForEntity(SERVER + "/MultiErrorCodeService/noClientErrorCode", requestJson, Object.class); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); mapResult = RestObjectMapperFactory.getRestObjectMapper().convertValue(e.getErrorData(), Map.class); TestMgr.check(mapResult.get("message"), "test message"); TestMgr.check(mapResult.get("code"), 400); TestMgr.check(mapResult.get("t400"), 400); } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/SchemeInterfaceJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import org.springframework.data.domain.Page; public interface SchemeInterfaceJaxrs { int add(int a, int b); int reduce(int a, int b); Page interfaceModel(Page model); } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestClientTimeout.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.validator.Student; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestClientTimeout implements CategorizedTestCase { private static RestOperations template = RestTemplateBuilder.create(); public void testAllTransport() throws Exception { testClientTimeOut(template); } private static void testClientTimeOut(RestOperations template) { String microserviceName = "jaxrs"; String cseUrlPrefix = "cse://" + microserviceName + "/clientreqtimeout/"; testClientTimeoutSayHi(template, cseUrlPrefix); testClientTimeoutAdd(template, cseUrlPrefix); } private static void testClientTimeoutSayHi(RestOperations template, String cseUrlPrefix) { Student student = new Student(); student.setName("timeout"); student.setAge(30); Student result = template.postForObject(cseUrlPrefix + "sayhello", student, Student.class); TestMgr.check("hello timeout 30", result); } private static void testClientTimeoutAdd(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "20"); boolean failed = false; // long failures = 0; // ServiceCombServerStats serviceCombServerStats = null; try { // serviceCombServerStats = getServiceCombServerStats(); // failures = serviceCombServerStats.getContinuousFailureCount(); template.postForObject(cseUrlPrefix + "add", params, Integer.class); } catch (InvocationException e) { failed = true; // implement timeout with same error code and message for rest and highway TestMgr.check(408, e.getStatus().getStatusCode()); // Request Timeout or Invocation Timeout TestMgr.check(true, e.getErrorData().toString().contains("Timeout.")); } TestMgr.check(true, failed); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.type.TypeReference; @Component public class TestCodeFirstJaxrs implements CategorizedTestCase { interface DownloadInf { ReadStreamPart testDeleteAfterFinished(String name, String content); } private static final String SERVICE_NAME = "jaxrs"; private static final String SCHEMA_ID = "codeFirst"; @RpcReference(microserviceName = SERVICE_NAME, schemaId = SCHEMA_ID) private DownloadInf downloadInf; @Override public void testAllTransport() throws Exception { testCodeFirstJaxrs(); testResponseLong(); } @Override public void testHighwayTransport() throws Exception { // test only once testInstanceIsolation(); } private void testInstanceIsolation() { AtomicInteger e503Business = new AtomicInteger(0); AtomicInteger e503CircuitBreaker = new AtomicInteger(0); for (int i = 0; i < 30; i++) { try { InvokerUtils.syncInvoke(SERVICE_NAME, SCHEMA_ID, "instanceIsolationTest", null, String.class); } catch (InvocationException e) { if (e.getStatusCode() == 503) { if ("CommonExceptionData [message=business]".equals(e.getErrorData().toString())) { e503Business.getAndIncrement(); } else if ("CommonExceptionData [message=instance isolation circuitBreaker is open.]".equals( e.getErrorData().toString())) { e503CircuitBreaker.getAndIncrement(); } else { TestMgr.fail("not expected message"); } } else { TestMgr.fail("not expected code"); } } } TestMgr.check(true, e503Business.get() >= 10); TestMgr.check(true, e503CircuitBreaker.get() >= 10); } @Override public void testRestTransport() throws Exception { testDeleteAfterFinished(); } private void testDeleteAfterFinished() throws Exception { ReadStreamPart part = downloadInf.testDeleteAfterFinished("hello", "hello content"); TestMgr.check(part.saveAsString().get(), "hello content"); File systemTempFile = new File(System.getProperty("java.io.tmpdir")); File file = new File(systemTempFile, "hello"); TestMgr.check(file.exists(), false); } private void testResponseLong() { Object result = InvokerUtils.syncInvoke(SERVICE_NAME, SCHEMA_ID, "responseLong", null, Object.class); TestMgr.check(result, Long.MAX_VALUE); } // invoke CodeFirstJaxrs private void testCodeFirstJaxrs() { Map swaggerArguments = new HashMap<>(); Map userMap = new HashMap<>(); User user = new User(); user.setName("hello"); userMap.put("user", user); swaggerArguments.put("userMap", userMap); TypeReference> type = new TypeReference>() { }; Map result = InvokerUtils.syncInvoke(SERVICE_NAME, SCHEMA_ID, "testUserMap", swaggerArguments, type.getType()); TestMgr.check(result.get("user").getName(), userMap.get("user").getName()); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrsReactive.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestCodeFirstJaxrsReactive implements CategorizedTestCase { interface AddOperation { CompletableFuture add(int a, int b); } @RpcReference(microserviceName = "jaxrs", schemaId = "codeFirst") AddOperation addOperation; @Override public void testAllTransport() throws Exception { final int count = 10; CountDownLatch latch = new CountDownLatch(count); AtomicInteger result = new AtomicInteger(0); for (int i = 0; i < count; i++) { new Thread(() -> addOperation.add(1, 2) .whenComplete((r, e) -> addOperation.add(r, r).whenComplete((r1, e1) -> { result.addAndGet(r1); latch.countDown(); }))).start(); } latch.await(3, TimeUnit.SECONDS); TestMgr.check(count * 6, result.get()); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestDynamicConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.util.Arrays; import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; @Component public class TestDynamicConfig implements BootListener { @InjectProperties(prefix = "jaxrstest.jaxrsclient") public static class Configuration { /* * 方法的 prefix 属性值 "override" 会覆盖标注在类定义的 @InjectProperties * 注解的 prefix 属性值。 * * keys属性可以为一个字符串数组,下标越小优先级越高。 * * 这里会按照如下顺序的属性名称查找配置属性,直到找到已被配置的配置属性,则停止查找: * 1) jaxrstest.jaxrsclient.override.high * 2) jaxrstest.jaxrsclient.override.low * * 测试用例: * jaxrstest.jaxrsclient.override.high: hello high * jaxrstest.jaxrsclient.override.low: hello low * 预期: * hello high */ @InjectProperty(prefix = "jaxrstest.jaxrsclient.override", keys = {"high", "low"}) public String strValue; /** * keys支持通配符,并在可以在将配置属性注入的时候指定通配符的代入对象。 * * 测试用例: * jaxrstest.jaxrsclient.k.value: 3 * 预期: * 3 */ @InjectProperty(keys = "${key}.value") public int intValue; /** * 通配符的代入对象可以是一个字符串List,优先级遵循数组元素下标越小优先级越高策略。 * * 测试用例: * jaxrstest.jaxrsclient.l1-1: 3.0 * jaxrstest.jaxrsclient.l1-2: 2.0 * * 预期: * 3.0 */ @InjectProperty(keys = "${full-list}") public float floatValue; /** * keys属性也支持多个通配符,优先级如下:首先通配符的优先级从左到右递减, * 然后如果通配符被代入List,遵循List中元素index越小优先级越高策略。 * * 测试用例: * jaxrstest.jaxrsclient.low-1.a.high-1.b: 1 * jaxrstest.jaxrsclient.low-1.a.high-2.b: 2 * jaxrstest.jaxrsclient.low-2.a.high-1.b: 3 * jaxrstest.jaxrsclient.low-2.a.high-2.b: 4 * 预期: * 1 */ @InjectProperty(keys = "${low-list}.a.${high-list}.b") public long longValue; /** * 可以通过注解的defaultValue属性指定默认值。如果字段未关联任何配置属性, * 定义的默认值会生效,否则默认值会被覆盖。 * * 测试用例: * 预期: * abc */ @InjectProperty(defaultValue = "abc") public String strDef; } @Override public void onAfterRegistry(BootEvent event) { Configuration configuration = SCBEngine.getInstance().getPriorityPropertyManager() .createConfigObject(Configuration.class, "key", "k", "low-list", Arrays.asList("low-1", "low-2"), "high-list", Arrays.asList("high-1", "high-2"), "full-list", Arrays.asList("l1-1", "l1-2") ); TestMgr.check(configuration.strValue, "hello high"); TestMgr.check(configuration.intValue, 3); TestMgr.check(configuration.floatValue, 3.0); TestMgr.check(configuration.longValue, 1); TestMgr.check(configuration.strDef, "abc"); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFileUploadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.core.MediaType; @Component public class TestFileUploadSchema implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { testUpload(RestTemplateBuilder.create(), "servicecomb://jaxrs/fileUpload"); } private void testUpload(RestOperations template, String cseUrlPrefix) throws IOException { String file1Content = "Hello World"; File file1 = File.createTempFile("jaxrstest1", ".txt"); FileUtils.writeStringToFile(file1, file1Content, StandardCharsets.UTF_8, false); testFileUpload(template, cseUrlPrefix, file1, file1Content); testFileAndStringUpload(template, cseUrlPrefix, file1, file1Content); } private void testFileUpload(RestOperations template, String cseUrlPrefix, File file1, String file1Content) throws IOException { String result1 = template.postForObject(cseUrlPrefix + "/upload1", new HttpEntity<>(new HashMap<>()), String.class); TestMgr.check("null file", result1); Map map = new HashMap<>(); map.put("file1", new FileSystemResource(file1)); String file2Content = "Hello EveryOne"; File file2 = File.createTempFile("测试2", ".txt"); FileUtils.writeStringToFile(file2, file2Content, StandardCharsets.UTF_8, false); map.put("file2", new FileSystemResource(file2)); String expect = String.format("%s:%s:%s\n" + "%s:%s:%s", file1.getName(), MediaType.TEXT_PLAIN, file1Content, file2.getName(), MediaType.TEXT_PLAIN, file2Content); String result2 = template.postForObject(cseUrlPrefix + "/upload1", new HttpEntity<>(map), String.class); TestMgr.check(expect, result2); } private void testFileAndStringUpload(RestOperations template, String cseUrlPrefix, File file1, String file1Content) { Map map = new HashMap<>(); String message = "hi"; map.put("file1", new FileSystemResource(file1)); map.put("message", message); HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); String expect = String.format("%s:%s:%s:%s", file1.getName(), MediaType.TEXT_PLAIN, file1Content, message); String result = template.postForObject(cseUrlPrefix + "/upload2", new HttpEntity<>(map, headers), String.class); TestMgr.check(expect, result); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; @Component public class TestFormRequestSchema implements CategorizedTestCase { private static final Logger LOGGER = LoggerFactory.getLogger(TestFormRequestSchema.class); private final RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { testFormRequestFail(); // testFormRequestFail会关闭连接,防止下个测试用例失败,睡眠2s Thread.sleep(2000); testFormRequestSuccess(); testFormRequestBufferSize(); } // formSize is less than default maxFormAttributeSize , success private void testFormRequestSuccess() { try { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); formData.add("formData", "a".repeat(512)); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); ResponseEntity responseEntity = restTemplate .postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); TestMgr.check(responseEntity.getBody(), "formRequest success : 512"); } catch (Throwable e) { LOGGER.error("testFormRequestSuccess-->", e); TestMgr.failed("", e); } } // formSize is greater than default maxFormAttributeSize , throw exception private void testFormRequestFail() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); formData.add("formData", "a".repeat(1688)); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); try { restTemplate.postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); TestMgr.fail("Size exceed allowed maximum capacity"); } catch (Throwable e) { TestMgr.check(e.getMessage().contains("Internal Server Error"), true); } } private void testFormRequestBufferSize() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); // we can not test a situation for form exceed max buffer size, because the netty buffer is very // big and the trunc can always be decoded and cached buffer size is always 0. formData.add("F0123456789001234567890012345678900123456789001234567890" + "0123456789001234567890012345678900123456789001234567890", "a".repeat(1020) // we can not test a situation for form exceed max buffer size, because the netty buffer is very // big and the trunc can always be decoded and cached buffer size is always 0. ); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); try { ResponseEntity responseEntity = restTemplate.postForEntity("cse://jaxrs/form/formLongName", requestEntity, String.class); TestMgr.check(responseEntity.getBody(), "formRequest success : 1020"); } catch (Throwable e) { LOGGER.error("testFormRequestBufferSize-->", e); TestMgr.failed("", e); } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestQueryParamSchema implements CategorizedTestCase { public interface QueryParamService { String testQueryEncode(String param); } private final RestOperations restTemplate = RestTemplateBuilder.create(); @RpcReference(microserviceName = "jaxrs", schemaId = "QueryParamSchema") private QueryParamService queryParamService; @Override public void testAllTransport() throws Exception { testQueryParamEncodingPlus(); } private void testQueryParamEncodingPlus() { // should encode + TestMgr.check("a+b", restTemplate.getForObject("servicecomb://jaxrs/queryParam/testQueryEncode?param={1}", String.class, "a+b")); // if not encoded, + should be blank TestMgr.check("a b", restTemplate.getForObject("servicecomb://jaxrs/queryParam/testQueryEncode?param=a+b", String.class)); // rpc should encode TestMgr.check("a+b", queryParamService.testQueryEncode("a+b")); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestQueryParamWithListSchema implements CategorizedTestCase { private RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { testMulti(); testCSV(); testSSV(); testPipes(); } @Override public void testRestTransport() throws Exception { testMultiRest(); testCSVRest(); testSSVRest(); testPipesRest(); } @Override // highway do not handle empty/default/null public void testHighwayTransport() throws Exception { testMultiHighway(); testCSVHighway(); testSSVHighway(); testPipesHighway(); } private void testCSVHighway() { TestMgr.check("null", restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?", String.class)); } private void testCSVRest() { TestMgr.check("0:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?", String.class)); } private void testSSV() { TestMgr.check("1:[1%202]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=1%202", String.class)); TestMgr.check("1:[%20]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=%20", String.class)); TestMgr.check("1:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=", String.class)); TestMgr.check("2:[1, 2]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList={1}", String.class, "1 2")); TestMgr.check("2:[, ]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList={1}", String.class, " ")); TestMgr.check("1:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=", String.class)); } private void testPipesHighway() { TestMgr.check("null", restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?", String.class)); } private void testPipesRest() { TestMgr.check("0:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?", String.class)); } private void testPipes() { TestMgr.check("2:[1, 2]", restTemplate .getForObject("cse://jaxrs/queryList/queryListPIPES?queryList={1}", String.class, "1|2")); TestMgr.check("2:[, ]", restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?queryList={1}", String.class, "|")); TestMgr.check("1:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?queryList=", String.class)); } private void testSSVHighway() { TestMgr.check("null", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?", String.class)); } private void testSSVRest() { TestMgr.check("0:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?", String.class)); } private void testCSV() { TestMgr.check("2:[1, 2]", restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?queryList=1,2", String.class)); TestMgr.check("2:[, ]", restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?queryList=,", String.class)); TestMgr.check("1:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?queryList=", String.class)); } private void testMultiHighway() { TestMgr.check("null", restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?", String.class)); } private void testMultiRest() { TestMgr.check("0:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?", String.class)); } private void testMulti() { TestMgr.check("2:[1, 2]", restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?queryList=1&queryList=2", String.class)); TestMgr.check("2:[, ]", restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?queryList=&queryList=", String.class)); TestMgr.check("1:[]", restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?queryList=", String.class)); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestReactiveSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.util.Map; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestReactiveSchema implements CategorizedTestCase { @SuppressWarnings("rawtypes") public void testRestTransport() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); try { restTemplate.getForObject("cse://jaxrs/reactive/testSyncInvokeInEventLoop?a=1&b=2", int.class); TestMgr.check(true, false); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 500); TestMgr.check(((Map) e.getErrorData()).get("message"), "Unexpected exception when processing jaxrs.ReactiveSchema.testSyncInvokeInEventLoop. " + "Can not execute sync logic in event loop."); } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestSchemeInterfaceJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; @Component public class TestSchemeInterfaceJaxrs implements CategorizedTestCase { @RpcReference(schemaId = "SchemeInterfaceJaxrs", microserviceName = "jaxrs") private SchemeInterfaceJaxrs jaxrs; public void testRestTransport() throws Exception { List contents = new ArrayList<>(); contents.add("hello"); Sort sort = Sort.by(new String[0]); Pageable pageable = PageRequest.of(1, 10, sort); Page pages = new PageImpl<>(contents, pageable, 1); Page result = jaxrs.interfaceModel(pages); TestMgr.check("hello", result.stream().findFirst().get()); } public void testAllTransport() throws Exception { TestMgr.check(3, jaxrs.add(1, 2)); TestMgr.check(0, jaxrs.add(-1, 1)); try { jaxrs.reduce(1, 3); TestMgr.failed("should throw exception", new Exception()); } catch (InvocationException e) { TestMgr.check( "Consumer method org.apache.servicecomb.demo.jaxrs.client.SchemeInterfaceJaxrs:reduce " + "not exist in contract, microserviceName=jaxrs, schemaId=SchemeInterfaceJaxrs.", ((CommonExceptionData) e.getError()).getMessage()); } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamPojoClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.beanParam; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.Invoker; import org.springframework.stereotype.Component; @Component public class BeanParamPojoClient implements CategorizedTestCase { private BeanParamTestServiceIntf beanParamTestServiceIntf; public BeanParamPojoClient() { beanParamTestServiceIntf = Invoker.createProxy("jaxrs", "beanParamTest", BeanParamTestServiceIntf.class); } @Override public void testRestTransport() throws Exception { testBeanParam(); testUpload(); } @Override public void testHighwayTransport() throws Exception { } @Override public void testAllTransport() throws Exception { testBeanParam(); } private void testBeanParam() { String result = beanParamTestServiceIntf.beanParameterTest("querySwaggerValue", 2, "pathSwaggerValue", 10, "extra"); TestMgr.check( "invocationContextConsistency=true|testBeanParameter=TestBeanParameter{queryStr='querySwaggerValue', headerInt=2, " + "pathStr='pathSwaggerValue', cookieLong=10}|extraQuery=extra", result); } private void testUpload() { BufferedInputStream bufferedInputStream0 = new BufferedInputStream(new ByteArrayInputStream("up0".getBytes(StandardCharsets.UTF_8))); BufferedInputStream bufferedInputStream1 = new BufferedInputStream(new ByteArrayInputStream("up1".getBytes(StandardCharsets.UTF_8))); BufferedInputStream bufferedInputStream2 = new BufferedInputStream(new ByteArrayInputStream("up2".getBytes(StandardCharsets.UTF_8))); String result = beanParamTestServiceIntf.beanParameterTestUpload( bufferedInputStream0, "queryTest", bufferedInputStream1, bufferedInputStream2, "ex"); TestMgr.check( "testBeanParameter=TestBeanParameterWithUpload{queryStr='queryTest'}|extraQuery=ex|up0=up0|up1=up1|up2=up2", result); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamRestTemplateClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.beanParam; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class BeanParamRestTemplateClient implements CategorizedTestCase { RestOperations restTemplate; public BeanParamRestTemplateClient() { restTemplate = RestTemplateBuilder.create(); } @Override public void testRestTransport() throws Exception { testBeanParam(); testUpload(); } @Override public void testHighwayTransport() throws Exception { } @Override public void testAllTransport() throws Exception { testBeanParam(); } private void testBeanParam() { HttpHeaders headers = new HttpHeaders(); headers.add("Cookie", "cookieSwaggerLong=11"); headers.add("headerSwaggerInt", "2"); HttpEntity requestEntity1 = new CseHttpEntity<>(headers); ResponseEntity result = restTemplate.exchange( "cse://jaxrs/beanParamTest/pathSwaggerValue/simple?querySwaggerStr=querySwaggerValue&extraQuery=extra", HttpMethod.GET, requestEntity1, String.class); TestMgr.check( "invocationContextConsistency=true|testBeanParameter=TestBeanParameter{queryStr='querySwaggerValue', headerInt=2, " + "pathStr='pathSwaggerValue', cookieLong=11}|extraQuery=extra", result.getBody()); } private void testUpload() { BufferedInputStream bufferedInputStream0 = new BufferedInputStream( new ByteArrayInputStream("up0".getBytes(StandardCharsets.UTF_8))); BufferedInputStream bufferedInputStream1 = new BufferedInputStream( new ByteArrayInputStream("up1".getBytes(StandardCharsets.UTF_8))); BufferedInputStream bufferedInputStream2 = new BufferedInputStream( new ByteArrayInputStream("up2".getBytes(StandardCharsets.UTF_8))); HashMap formData = new HashMap<>(); formData.put("up0", bufferedInputStream0); formData.put("up1", bufferedInputStream1); formData.put("up2", bufferedInputStream2); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntity> entity = new HttpEntity<>(formData, headers); String result = restTemplate.postForObject("cse://jaxrs/beanParamTest/upload?query=fromTemplate&extraQuery=ex", entity, String.class); TestMgr.check( "testBeanParameter=TestBeanParameterWithUpload{queryStr='fromTemplate'}|extraQuery=ex|up0=up0|up1=up1|up2=up2", result); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamTestServiceIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.beanParam; import java.io.InputStream; public interface BeanParamTestServiceIntf { String beanParameterTest(String querySwaggerStr, Integer headerSwaggerInt, String pathSwaggerStr, long cookieSwaggerLong, String extraQuery); String beanParameterTestUpload(InputStream up0, String query, InputStream up1, InputStream up2, String extraQuery); } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/injectBean/TestInjectBeanSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.injectBean; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestInjectBeanSchema implements CategorizedTestCase { RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { boolean result = restTemplate.getForObject("cse://jaxrs/injectSet", boolean.class); TestMgr.check(true, result); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.pojoDefault; public class DefaultModel { private int index; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModelServiceClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.pojoDefault; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.jaxrs.server.pojoDefault.DefaultResponseModel; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.web.client.RestOperations; public class DefaultModelServiceClient { private static RestOperations template = RestTemplateBuilder.create(); private static String urlPrefix = "cse://jaxrs/DefaultModelService"; public static void run() { // highway do not support this feature InMemoryDynamicPropertiesSource.update("servicecomb.references.transport.jaxrs", "rest"); testDefaultModelService(); } private static void testDefaultModelService() { DefaultModel model = new DefaultModel(); model.setIndex(400); DefaultResponseModel result = template.postForObject(urlPrefix + "/model", model, DefaultResponseModel.class); TestMgr.check(result.getAge(), 200); TestMgr.check(result.getIndex(), 400); TestMgr.check(result.getName(), "World"); TestMgr.check(result.getDesc(), null); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.client.validation; import java.util.ArrayList; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.jaxrs.server.validation.ValidationModel; import org.apache.servicecomb.demo.validator.Teacher; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.core.Response.Status; public class ValidationServiceClient { private static RestOperations template = RestTemplateBuilder.create(); private static String urlPrefix = "cse://jaxrs/ValidationService"; public static void run() { // highway do not support this feature InMemoryDynamicPropertiesSource.update("servicecomb.references.transport.jaxrs", "rest"); testValidation(); } private static void testValidation() { ValidationModel model = new ValidationModel(); model.setAge(20); model.setMembers(new ArrayList<>()); model.setName("name"); ValidationModel result = template.postForObject(urlPrefix + "/validate", model, ValidationModel.class); TestMgr.check(result.getAge(), 20); TestMgr.check(result.getName(), "name"); TestMgr.check(result.getMembers().size(), 0); try { model.setAge(null); template.postForObject(urlPrefix + "/validate", model, ValidationModel.class); TestMgr.check(false, true); } catch (InvocationException e) { TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); TestMgr.check(e.getErrorData().toString().contains("propertyPath=errorCode.request.age"), true); } try { model.setAge(20); model.setMembers(null); template.postForObject(urlPrefix + "/validate", model, ValidationModel.class); TestMgr.check(false, true); } catch (InvocationException e) { TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); TestMgr.check(e.getErrorData().toString().contains("propertyPath=errorCode.request.members"), true); } try { template.postForObject(urlPrefix + "/validate", null, ValidationModel.class); TestMgr.check(false, true); } catch (InvocationException e) { TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); TestMgr.check(e.getErrorData().toString().contains("propertyPath=errorCode.request"), true); } String strResult = template.getForObject(urlPrefix + "/validateQuery?name=a", String.class); TestMgr.check(strResult, "a"); try { template.getForObject(urlPrefix + "/validateQuery", String.class); TestMgr.check(false, true); } catch (InvocationException e) { TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); TestMgr.check(((CommonExceptionData) e.getErrorData()).getMessage().contains("Parameter is not valid"), true); } Teacher teacher = new Teacher(); teacher.setName("teacher"); teacher.setAge("20"); Teacher response = template.postForObject(urlPrefix + "/sayTeacherInfo", teacher, Teacher.class); TestMgr.check(response.getName(), "teacher"); try { teacher = new Teacher(); teacher.setAge("20"); template.postForObject(urlPrefix + "/sayTeacherInfo", teacher, Teacher.class); TestMgr.fail("Name should not empty"); } catch (InvocationException e) { TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(e.getErrorData().toString().contains("must not be blank"), true); } // jax-rs body default required = false response = template.postForObject(urlPrefix + "/sayTeacherInfo", null, Teacher.class); TestMgr.check(null, response); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Null; public class DefaultResponseModel { @Min(20) @Max(2000) @Null private Integer age = 200; @Min(2) @Max(30) @Null private String name = "World"; private int index; private String desc = "Hello"; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } @Override public String toString() { return "index=" + index + ";name=" + name + ";age=" + age; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: jaxrstest name: jaxrsClient version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 enableSwaggerRegistration: true matchGroup: instanceIsolationTest: | matches: - apiPath: prefix: "/codeFirstJaxrs/instanceIsolationTest" instanceIsolation: instanceIsolationTest: | minimumNumberOfCalls: 10 slidingWindowSize: 20 slidingWindowType: COUNT_BASED failureRateThreshold: 50 slowCallRateThreshold: 100 slowCallDurationThreshold: 3000 waitDurationInOpenState: 200 permittedNumberOfCallsInHalfOpenState: 10 request: timeout: 30000 jaxrs: timeout: 30000 clientreqtimeout: timeout: 30000 sayHello: timeout: 30000 add: timeout: 1000 invocation: timeout: check: enabled: false # use service center to find schema info openAPI: registry: # enable service center OpenAPI registry, and need set enableSwaggerRegistration: true registry: enabled: true # disable instance OpenAPI registry instance: enabled: false # test configurations. you can choose any implementation. default using local. # using nacos configuration # nacos: # serverAddr: http://127.0.0.1:8848 # group: jaxrstest # dataId: jaxrsclient # namespace: public # contentType: properties # can be properties, yaml, raw. If using raw, property [group].[dataId]=value. # addPrefix: false # if true [group].[dataId] will added as properties/yaml items prefix. Will not influence raw. # for integration test not install any config server jaxrstest: jaxrsclient: override.high: hello high override.low: hello low k.value: 3 l1-1: 3.0 l1-2: 2.0 low-1.a.high-1.b: 1 low-1.a.high-2.b: 2 low-2.a.high-1.b: 3 low-2.a.high-2.b: 4 # test configurations. ================================================ FILE: demo/demo-jaxrs/jaxrs-client/src/test/java/org/apache/servicecomb/demo/jaxrs/JaxrsIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = JaxrsClient.class) public class JaxrsIT { @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { JaxrsClient.run(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-jaxrs 3.4.0-SNAPSHOT jaxrs-server Java Chassis::Demo::JAXRS::Server org.apache.servicecomb.demo demo-schema org.glassfish.jersey.core jersey-client org.glassfish.jersey.core jersey-common org.apache.servicecomb swagger-invocation-validator org.apache.servicecomb.demo.jaxrs.JaxrsServer org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.demo.RestObjectMapperWithStringMapper; import org.apache.servicecomb.demo.RestObjectMapperWithStringMapperNotWriteNull; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class JaxrsServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(JaxrsServer.class).web(WebApplicationType.NONE).run(args); RestObjectMapperFactory.setDefaultRestObjectMapper(new RestObjectMapperWithStringMapper()); RestObjectMapperFactory.setConsumerWriterMapper(new RestObjectMapperWithStringMapperNotWriteNull()); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore; import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.vertx.core.json.JsonObject; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.CookieParam; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "codeFirst") @Path("/codeFirstJaxrs") @Produces(MediaType.APPLICATION_JSON) public class CodeFirstJaxrs { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = User.class)), description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @Path("/cseResponse") @GET public Response cseResponse(InvocationContext c1) { Response response = Response.createSuccess(Status.ACCEPTED, new User()); response.setHeader("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); response.setHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return response; } @Path("/testUserMap") @POST public Map testUserMap(Map userMap) { return userMap; } @Path("/textPlain") @POST @Consumes(MediaType.TEXT_PLAIN) public String textPlain(String body) { return body; } @Path("/bytes") @POST public byte[] bytes(byte[] input) { input[0] = (byte) (input[0] + 1); return input; } @Path("/addDate") @POST public Date addDate(@FormParam("date") Date date, @QueryParam("seconds") long seconds) { return new Date(date.getTime() + seconds * 1000); } @GET public int defaultPath() { return 100; } @Path("/add") @POST public int add(@FormParam("a") int a, @FormParam("b") int b) { return a + b; } @Path("/reduce") @GET @Parameters({@Parameter(name = "a", schema = @Schema(implementation = int.class), in = ParameterIn.QUERY)}) public int reduce(HttpServletRequest request, @CookieParam("b") int b) { int a = Integer.parseInt(request.getParameter("a")); return a - b; } @Path("/sayhello") @POST public Person sayHello(Person user) { user.setName("hello " + user.getName()); return user; } @SuppressWarnings("unchecked") @Path("/testrawjson") @POST public String testRawJsonString(String jsonInput) { Map person; try { person = RestObjectMapperFactory.getRestObjectMapper() .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; } return "hello " + person.get("name"); } @Path("/saysomething") @POST public String saySomething(@HeaderParam("prefix") String prefix, Person user) { return prefix + " " + user.getName(); } @Path("/sayhi/{name}") @PUT public String sayHi(@PathParam("name") String name) { ContextUtils.getInvocationContext().setStatus(202); return name + " sayhi"; } @Path("/sayhi/{name}/v2") @PUT public String sayHi2(@PathParam("name") String name) { return name + " sayhi 2"; } @Path("/istrue") @GET public boolean isTrue() { return true; } @Path("/addstring") @DELETE @Produces(MediaType.TEXT_PLAIN) public String addString(@QueryParam("s") List s) { StringBuilder result = new StringBuilder(); for (String x : s) { result.append(x); } return result.toString(); } @Path("/ignore") @POST public OutputModelForTestIgnore testModelWithIgnoreField(InputModelForTestIgnore input) { return new OutputModelForTestIgnore("output_id", input.getInputId(), input.getContent(), input.getInputObject(), input.getInputJsonObject(), input.getInputIgnoreInterface(), new Person("outputSomeone"), new JsonObject("{\"OutputJsonKey\" : \"OutputJsonValue\"}"), () -> { }); } @SuppressWarnings("unchecked") @Path("/rawJsonAnnotation") @POST public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { Map person; try { person = RestObjectMapperFactory.getRestObjectMapper() .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; } return "hello " + person.get("name"); } @Path("/traceId") @GET public String getTraceId() { return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); } @GET @Path("/responseLong") @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Object.class)), description = "") public Response responseLong() { return Response.createSuccess(Long.MAX_VALUE); } @Path("/download/testDeleteAfterFinished") @GET public Part testDeleteAfterFinished(@QueryParam("name") String name, @QueryParam("content") String content) throws IOException { File file = createTempFile(name, content); return new FilePart(null, file) .setDeleteAfterFinished(true) .setSubmittedFileName(name); } @Path("/instanceIsolationTest") @GET public String instanceIsolationTest() { throw new InvocationException(503, "", "business"); } private File createTempFile(String name, String content) throws IOException { File systemTempFile = new File(System.getProperty("java.io.tmpdir")); File file = new File(systemTempFile, name); FileUtils.write(file, content, StandardCharsets.UTF_8, false); return file; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ComputeImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import java.nio.charset.StandardCharsets; import java.util.Map; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "compute") @Path("/compute") @Produces(MediaType.APPLICATION_JSON) public class ComputeImpl { private static final Logger LOGGER = LoggerFactory.getLogger(ComputeImpl.class); @Path("/add") @POST public int add(@FormParam("a") int a, @FormParam("b") int b) { return a + b; } @Path("/reduce") @GET @Parameters(value = {@Parameter(in = ParameterIn.QUERY, name = "a", schema = @Schema(implementation = int.class)), @Parameter(in = ParameterIn.QUERY, name = "b", schema = @Schema(implementation = int.class))}) public int reduce(@Context HttpServletRequest request) { int a = Integer.parseInt(request.getParameter("a")); int b = Integer.parseInt(request.getParameter("b")); return a - b; } @Path("/sayhello") @POST public Person sayHello(Person user) { user.setName("hello " + user.getName()); return user; } @SuppressWarnings("unchecked") @Path("/testrawjson") @POST public String testRawJsonString(String jsonInput) { Map person; try { person = RestObjectMapperFactory.getRestObjectMapper() .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; } return "hello " + person.get("name"); } @Path("/saysomething") @POST public String saySomething(@HeaderParam("prefix") String prefix, Person user) { return prefix + " " + user.getName(); } @Path("/sayhi/{name}") @PUT public void sayHi(@PathParam("name") String name) { LOGGER.info(name + " sayhi"); ContextUtils.getInvocationContext().setStatus(202); } @Path("/sayhi/{name}/v2") @PUT public void sayHi2(@PathParam("name") String name) { LOGGER.info(name + " sayhi 2"); } @Path("/sayhei") @DELETE public void sayHei(@QueryParam("name") String name) { LOGGER.info(name + " sayhei"); } @Path("/istrue") @GET public boolean isTrue() { return true; } @Path("/addstring") @DELETE @Produces(MediaType.TEXT_PLAIN) public String addString(@QueryParam("s") String[] s) { StringBuilder result = new StringBuilder(); for (String x : s) { result.append(x); } return result.toString(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FileUploadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.provider.rest.common.RestSchema; import jakarta.servlet.http.Part; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; /** * Test and illustrate JaxRS uploads. * * Use @FormParam to annotate a multipart/form-data item. */ @RestSchema(schemaId = "FileUploadSchema") @Path("/fileUpload") public class FileUploadSchema { @Path("/upload1") @POST @Produces(MediaType.TEXT_PLAIN) public String fileUpload1(@FormParam("file1") Part file1, @FormParam("file2") Part file2) throws IOException { if (file1 == null || file2 == null) { return "null file"; } try (InputStream is1 = file1.getInputStream(); InputStream is2 = file2.getInputStream()) { String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); String content2 = IOUtils.toString(is2, StandardCharsets.UTF_8); return String.format("%s:%s:%s\n" + "%s:%s:%s", file1.getSubmittedFileName(), file1.getContentType(), content1, file2.getSubmittedFileName(), file2.getContentType(), content2); } } @Path("/upload2") @POST @Produces(MediaType.TEXT_PLAIN) public String fileUpload2(@FormParam("file1") Part file1, @FormParam("message") String message) throws IOException { try (InputStream is1 = file1.getInputStream()) { String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); return String.format("%s:%s:%s:%s", file1.getSubmittedFileName(), file1.getContentType(), content1, message); } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "FormRequestSchema") @Path("/form") @Produces(MediaType.APPLICATION_JSON) public class FormRequestSchema { @Path("/formRequest") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String formRequestSuccess(@FormParam("formData") String formData) throws Exception { return "formRequest success : " + formData.length(); } @Path("/formLongName") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String formLongName(@FormParam("F0123456789001234567890012345678900123456789001234567890" + "0123456789001234567890012345678900123456789001234567890") String formData) throws Exception { return "formRequest success : " + formData.length(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxRSDefaultValues.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "JaxRSDefaultValues") @Path("/JaxRSDefaultValues") @Produces(MediaType.APPLICATION_JSON) public class JaxRSDefaultValues { @Path("/form") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String form(@DefaultValue("20") @FormParam("a") int a, @DefaultValue("bobo") @FormParam("b") String b) { return "Hello " + a + b; } @Path("/header") @POST public String header(@DefaultValue("20") @HeaderParam("a") int a, @DefaultValue("bobo") @HeaderParam("b") String b, @DefaultValue("30") @HeaderParam("c") Integer c) { return "Hello " + a + b + c; } @Path("/query") @GET public String query(@DefaultValue("20") @QueryParam("a") int a, @DefaultValue("bobo") @QueryParam("b") String b, @DefaultValue("40") @QueryParam("c") Integer c, @QueryParam("d") int d) { return "Hello " + a + b + c + d; } @Path("/query2") @GET public String query2(@QueryParam("e") int e, @DefaultValue("20") @QueryParam("a") int a, @DefaultValue("bobo") @QueryParam("b") String b, @DefaultValue("40") @QueryParam("c") Integer c, @Min(value = 20) @Max(value = 30) @QueryParam("d") int d) { return "Hello " + a + b + c + d + e; } @Path("/query3") @GET public String query3(@QueryParam("a") @Min(value = 20) int a, @QueryParam("b") String b) { return "Hello " + a + b; } @Path("/packages") @GET public String queryPackages(HttpServletRequest httpRequest, @Max(value = 2147483647L) @Min(value = -1L) @NotNull @QueryParam("pageNo") Integer pageNo, @Max(value = 2147483647L) @Min(value = -1L) @NotNull @QueryParam("pageSize") Integer pageSize, @Size(max = 64, min = 0) @QueryParam("packageName") String packageName, @Max(value = 127L) @Min(value = 0L) @QueryParam("packageType") Integer packageType, @Max(value = 2147483647L) @Min(value = 1L) @QueryParam("roleID") Integer roleID, @Max(value = 2147483647L) @Min(value = 1L) @QueryParam("categoryID") Integer categoryID, @Max(value = 127L) @Min(value = 0L) @QueryParam("appType") @DefaultValue("1") Integer appType, @Max(value = 2L) @Min(value = 1L) @QueryParam("packageScope") Integer packageScope) { return "" + appType; } @Path("/javaprimitiveint") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String jaxRsJavaPrimitiveInt(@FormParam("a") int a, @DefaultValue("bobo") @FormParam("b") String b) { return "Hello " + a + b; } @Path("/javaprimitivenumber") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String jaxRsJavaPrimitiveNumber(@QueryParam("a") float a, @QueryParam("b") boolean b) { return "Hello " + a + b; } @Path("/javaprimitivestr") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String jaxRsJavaPrimitiveStr(@FormParam("b") int b, @FormParam("a") String a) { if (a == null || a.equals("")) { return "Hello"; } return "Hello " + b + a; } @Path("/javaprimitivecomb") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String jaxRsJavaPrimitiveCombnation(@QueryParam("a") Integer a, @QueryParam("b") Float b) { return "Hello " + a + b; } @Path("/allprimitivetypes") @POST public String allprimitivetypes(@QueryParam("pBoolean") boolean pBoolean, @QueryParam("pChar") char pChar, @QueryParam("pByte") byte pByte, @QueryParam("pShort") short pShort, @QueryParam("pInt") int pInt, @QueryParam("pLong") long pLong, @QueryParam("pFloat") float pFloat, @QueryParam("pDouble") double pDouble, @QueryParam("pDoubleWrap") Double pDoubleWrap) { return "Hello " + pBoolean + "," + pChar + "," + pByte + "," + pShort + "," + pInt + "," + pLong + "," + pFloat + "," + pDouble + "," + pDoubleWrap; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "QueryParamSchema") @Path("/queryParam") public class QueryParamSchema { @Path("testQueryEncode") @GET public String testQueryEncode(@QueryParam("param") String param) { return param; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import java.util.List; import org.apache.servicecomb.provider.rest.common.RestSchema; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.enums.ParameterStyle; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; @RestSchema(schemaId = "QueryParamWithListSchema") @Path("/queryList") public class QueryParamWithListSchema { @Path("queryListCSV") @GET public String queryListCSV( @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.FORM, explode = Explode.FALSE) @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("queryListSSV") @GET public String queryListSSV( @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("queryListPIPES") @GET public String queryListPIPES( @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("queryListMULTI") @GET public String queryListMULTI( @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.FORM, explode = Explode.TRUE) @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ReactiveSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ReactiveSchema") @Path("/reactive") @Produces(MediaType.APPLICATION_JSON) public class ReactiveSchema { // reactive is configured to run in event-loop thread pool @Path("/testSyncInvokeInEventLoop") @GET public int testSyncInvokeInEventLoop(@QueryParam("a") int a, @QueryParam("b") int b) { RestOperations client = RestTemplateBuilder.create(); return client.getForObject("cse://jaxrs/compute/reduce?a=1&b=2", int.class); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/RequestClientTimeOut.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.demo.validator.Student; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "clientreqtimeout") @Path("/clientreqtimeout") @Produces(MediaType.APPLICATION_JSON) public class RequestClientTimeOut { @Path("/add") @POST public int add(@FormParam("a") int a, @FormParam("b") int b) throws InterruptedException { Thread.sleep(2000); return a + b; } @Path("/sayhello") @POST public Student sayHello(Student student) throws InterruptedException { student.setName("hello " + student.getName()); student.setAge(student.getAge()); return student; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import jakarta.validation.constraints.Min; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.RequestParam; @Path("/jaxrs/schemaInterface") @Produces(MediaType.APPLICATION_JSON) public interface SchemeInterfaceJaxrs { @Path("/add") @GET int add(@Min(-100) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); @Path("/interfaceModel") @GET Page interfaceModel(Page model); } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrsImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.data.domain.Page; @RestSchema(schemaId = "SchemeInterfaceJaxrs", schemaInterface = SchemeInterfaceJaxrs.class) public class SchemeInterfaceJaxrsImpl implements SchemeInterfaceJaxrs { @Override public int add(int a, int b) { return a + b; } public int reduce(int a, int b) { return a - b; } public int add(String a, String b) { return Integer.parseInt(a) + Integer.parseInt(b); } @Override public Page interfaceModel(Page model) { return model; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/Validator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.demo.validator.Student; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.hibernate.validator.constraints.Length; @RestSchema(schemaId = "validator") @Path("/validator") @Produces(MediaType.APPLICATION_JSON) public class Validator { @Path("/add") @POST public int add(@FormParam("a") int a, @Min(20) @FormParam("b") int b) { return a + b; } @Path("/sayhi/{name}") @PUT public String sayHi(@Length(min = 3) @PathParam("name") String name) { ContextUtils.getInvocationContext().setStatus(202); return name + " sayhi"; } @Path("/sayhello") @POST public Student sayHello(@Valid Student student) { student.setName("hello " + student.getName()); student.setAge(student.getAge()); return student; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/BeanParamTestService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.beanParam; import java.io.IOException; import java.util.Scanner; import jakarta.servlet.http.Part; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; @RestSchema(schemaId = "beanParamTest") @Path("/beanParamTest") public class BeanParamTestService { @Path("/{pathSwaggerStr}/simple") @GET public String beanParameterTest(InvocationContext invocationContext, @BeanParam TestBeanParameter testBeanParameter, @QueryParam("extraQuery") String extraQuery) { return String.format("invocationContextConsistency=%b|testBeanParameter=%s|extraQuery=%s", ContextUtils.getInvocationContext() == invocationContext, testBeanParameter.toString(), extraQuery); } @Path("/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) @POST public String beanParameterTestUpload(@FormParam("up0") Part up0, @BeanParam TestBeanParameterWithUpload testBeanParameter, @QueryParam("extraQuery") String extraQuery) throws IOException { return String.format("testBeanParameter=%s|extraQuery=%s|up0=%s|up1=%s|up2=%s", testBeanParameter.toString(), extraQuery, getUploadContent(up0), getUploadContent(testBeanParameter.getUp1()), getUploadContent(testBeanParameter.getUp2())); } public String getUploadContent(Part upload) throws IOException { StringBuilder result = new StringBuilder(); try (Scanner scanner = new Scanner(upload.getInputStream())) { while (scanner.hasNext()) { result.append(scanner.next()); } } return result.toString(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.beanParam; import jakarta.ws.rs.CookieParam; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.QueryParam; import com.fasterxml.jackson.annotation.JsonIgnore; public class TestBeanParameter { @DefaultValue("defaultQueryValue") @QueryParam("querySwaggerStr") private String queryStr; private Integer headerInt; private String pathStr; @CookieParam("cookieSwaggerLong") private long cookieLong; @JsonIgnore private String ignoredField; public String getQueryStr() { return queryStr; } public void setQueryStr(String queryStr) { this.queryStr = queryStr; } public Integer getHeaderInt() { return headerInt; } @DefaultValue("12") @HeaderParam("headerSwaggerInt") public void setHeaderInt(Integer headerInt) { this.headerInt = headerInt; } public String getPathStr() { return pathStr; } @PathParam("pathSwaggerStr") public void setPathStr(String pathStr) { this.pathStr = pathStr; } public long getCookieLong() { return cookieLong; } public void setCookieLong(long cookieLong) { this.cookieLong = cookieLong; } public String getIgnoredField() { return ignoredField; } public void setIgnoredField(String ignoredField) { this.ignoredField = ignoredField; } @Override public String toString() { final StringBuilder sb = new StringBuilder("TestBeanParameter{"); sb.append("queryStr='").append(queryStr).append('\''); sb.append(", headerInt=").append(headerInt); sb.append(", pathStr='").append(pathStr).append('\''); sb.append(", cookieLong=").append(cookieLong); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameterWithUpload.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.beanParam; import jakarta.servlet.http.Part; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.QueryParam; public class TestBeanParameterWithUpload { @QueryParam("query") private String queryStr; @FormParam("up1") private Part up1; private Part up2; public String getQueryStr() { return queryStr; } public void setQueryStr(String queryStr) { this.queryStr = queryStr; } public Part getUp1() { return up1; } public void setUp1(Part up1) { this.up1 = up1; } public Part getUp2() { return up2; } @FormParam("up2") public void setUp2(Part up2) { this.up2 = up2; } @Override public String toString() { final StringBuilder sb = new StringBuilder("TestBeanParameterWithUpload{"); sb.append("queryStr='").append(queryStr).append('\''); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.injectBean; import org.springframework.stereotype.Component; @Component public class InjectBean { private boolean set = true; public boolean isSet() { return set; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.injectBean; import java.util.List; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher; @RestSchema(schemaId = "InjectBeanSchema") @Path("/") public class InjectBeanSchema { @Path("/injectSet") @GET public boolean injectSet() { List services = SPIServiceUtils.getOrLoadSortedService(VertxHttpDispatcher.class); for (VertxHttpDispatcher service : services) { if (service instanceof InjectBeanVertxHttpDispatcher) { return ((InjectBeanVertxHttpDispatcher) service).isSet(); } } return false; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanVertxHttpDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.injectBean; import org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher; import org.springframework.beans.factory.annotation.Autowired; import io.vertx.ext.web.Router; public class InjectBeanVertxHttpDispatcher implements VertxHttpDispatcher { private InjectBean injectBean; @Autowired public void setInjectBean(InjectBean injectBean) { this.injectBean = injectBean; } @Override public void init(Router router) { } public boolean isSet() { return injectBean.isSet(); } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/MultiErrorCodeService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.multiErrorCode; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.demo.multiErrorCode.MultiRequest; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse200; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse400; import org.apache.servicecomb.demo.multiErrorCode.MultiResponse500; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "MultiErrorCodeService") @Path("MultiErrorCodeService") public class MultiErrorCodeService { @Path("/errorCode") @POST @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MultiResponse200.class)), description = ""), @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = MultiResponse400.class)), description = ""), @ApiResponse(responseCode = "500", content = @Content(schema = @Schema(implementation = MultiResponse500.class)), description = "")}) public MultiResponse200 errorCode(MultiRequest request) { if (request.getCode() == 400) { MultiResponse400 r = new MultiResponse400(); r.setCode(400); r.setMessage("bad request"); throw new InvocationException(jakarta.ws.rs.core.Response.Status.BAD_REQUEST, r); } else if (request.getCode() == 500) { MultiResponse500 r = new MultiResponse500(); r.setCode(500); r.setMessage("internal error"); throw new InvocationException(jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, r); } else { MultiResponse200 r = new MultiResponse200(); r.setCode(200); r.setMessage("success result"); return r; } } @Path("/errorCodeWithHeader") @POST @ApiResponses({ @ApiResponse(responseCode = "200", headers = { @Header(name = "x-code", schema = @Schema(implementation = String.class))}, content = @Content(schema = @Schema(implementation = MultiResponse200.class)), description = ""), @ApiResponse(responseCode = "400", headers = { @Header(name = "x-code", schema = @Schema(implementation = String.class))}, content = @Content(schema = @Schema(implementation = MultiResponse400.class)), description = ""), @ApiResponse(responseCode = "500", headers = { @Header(name = "x-code", schema = @Schema(implementation = String.class))}, content = @Content(schema = @Schema(implementation = MultiResponse500.class)), description = "")}) public Response errorCodeWithHeader(MultiRequest request) { Response response = new Response(); if (request.getCode() == 400) { MultiResponse400 r = new MultiResponse400(); r.setCode(400); r.setMessage("bad request"); response.setStatus(Status.BAD_REQUEST); // If got many types for different status code, we can only using InvocationException for failed error code like 400-500. // The result for Failed Family(e.g. 400-500), can not set return value as target type directly or will give exception. response.setResult(new InvocationException(Status.BAD_REQUEST, r)); response.setHeader("x-code", "400"); } else if (request.getCode() == 500) { MultiResponse500 r = new MultiResponse500(); r.setCode(500); r.setMessage("internal error"); response.setStatus(Status.INTERNAL_SERVER_ERROR); response.setResult(new InvocationException(Status.INTERNAL_SERVER_ERROR, r)); response.setHeader("x-code", "500"); } else { MultiResponse200 r = new MultiResponse200(); r.setCode(200); r.setMessage("success result"); response.setStatus(Status.OK); // If error code is OK family(like 200), we can use the target type. response.setResult(r); response.setHeader("x-code", "200"); } return response; } // using JAX-RS providers, users need to add dependencies for implementations, see pom for an example. @Path("/errorCodeWithHeaderJAXRS") @POST @ApiResponses({ @ApiResponse(responseCode = "200", headers = { @Header(name = "x-code", schema = @Schema(implementation = String.class))}, content = @Content(schema = @Schema(implementation = MultiResponse200.class)), description = ""), @ApiResponse(responseCode = "400", headers = { @Header(name = "x-code", schema = @Schema(implementation = String.class))}, content = @Content(schema = @Schema(implementation = MultiResponse400.class)), description = ""), @ApiResponse(responseCode = "500", headers = { @Header(name = "x-code", schema = @Schema(implementation = String.class))}, content = @Content(schema = @Schema(implementation = MultiResponse500.class)), description = "")}) public jakarta.ws.rs.core.Response errorCodeWithHeaderJAXRS(MultiRequest request) { jakarta.ws.rs.core.Response response; if (request.getCode() == 400) { MultiResponse400 r = new MultiResponse400(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); // If got many types for different status code, we can only using InvocationException for failed error code like 400-500. // The result for Failed Family(e.g. 400-500), can not set return value as target type directly or will give exception. response = jakarta.ws.rs.core.Response.status(Status.BAD_REQUEST) .entity(new InvocationException(Status.BAD_REQUEST, r)) .header("x-code", "400") .build(); } else if (request.getCode() == 500) { MultiResponse500 r = new MultiResponse500(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); response = jakarta.ws.rs.core.Response.status(Status.INTERNAL_SERVER_ERROR) .entity(new InvocationException(Status.INTERNAL_SERVER_ERROR, r)) .header("x-code", "500") .build(); } else { MultiResponse200 r = new MultiResponse200(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); // If error code is OK family(like 200), we can use the target type. response = jakarta.ws.rs.core.Response.status(Status.OK) .entity(r) .header("x-code", "200") .build(); } return response; } @Path("/noClientErrorCode") @POST @ApiResponses({ @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = NoClientErrorCode400.class)), description = "")}) public List noClientErrorCode(MultiRequest request) { if (request.getCode() == 400) { NoClientErrorCode400 r = new NoClientErrorCode400(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); r.setT400(400); throw new InvocationException(Status.BAD_REQUEST, r); } else { NoClientErrorCode200 r = new NoClientErrorCode200(); r.setCode(request.getCode()); r.setMessage(request.getMessage()); r.setT200(200); List result = new ArrayList<>(); result.add(r); return result; } } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode200.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.multiErrorCode; public class NoClientErrorCode200 { private String message; private int code; private long t200; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public long getT200() { return t200; } public void setT200(long t200) { this.t200 = t200; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode400.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.multiErrorCode; public class NoClientErrorCode400 { private String message; private int code; private long t400; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public long getT400() { return t400; } public void setT400(long t400) { this.t400 = t400; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultModelService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "DefaultModelService") @Path("DefaultModelService") public class DefaultModelService { @Path("/model") @POST public DefaultResponseModel errorCode(DefaultRequestModel request) { DefaultResponseModel model = new DefaultResponseModel(); model.setIndex(request.getIndex()); model.setAge(request.getAge()); model.setName(request.getName()); model.setDesc(null); return model; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultRequestModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Null; public class DefaultRequestModel { @Min(20) @Max(2000) @Null private Integer age = 200; @Min(2) @Max(30) @Null private String name = "World"; private int index; private String desc; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } @Override public String toString() { return "index=" + index + ";name=" + name + ";age=" + age; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.pojoDefault; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Null; public class DefaultResponseModel { @Min(20) @Max(2000) @Null private Integer age = 200; @Min(2) @Max(30) @Null private String name = "World"; private int index; private String desc; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } @Override public String toString() { return "index=" + index + ";name=" + name + ";age=" + age; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.validation; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; import org.apache.servicecomb.demo.validator.Teacher; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "ValidationService") @Path("ValidationService") public class ValidationService { @Path("/validate") @POST public ValidationModel errorCode(@NotNull @Valid ValidationModel request) { return request; } @Path("/validateQuery") @GET public String queryValidate(@NotEmpty @QueryParam("name") String name) { return name; } @Path("/sayTeacherInfo") @POST public Teacher sayTeacherInfo(@Valid Teacher teacher) { return teacher; } } ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.jaxrs.server.injectBean.InjectBeanVertxHttpDispatcher ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: jaxrstest name: jaxrs version: 0.0.2 registry: sc: address: http://127.0.0.1:30100 enableSwaggerRegistration: true rest: address: 0.0.0.0:8080 server: maxFormAttributeSize: 1024 # for testing, and bigger than netty buffer allocator maxFormBufferedBytes: 100 highway: address: 0.0.0.0:7070 uploads: directory: target codec: printErrorMessage: true executors.Provider.ReactiveSchema: servicecomb.executor.reactive invocation: timeout: check: enabled: false # use service center to find schema info openAPI: registry: # enable service center OpenAPI registry, and need set enableSwaggerRegistration: true registry: enabled: true # disable instance OpenAPI registry instance: enabled: false ================================================ FILE: demo/demo-jaxrs/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-jaxrs Java Chassis::Demo::JAXRS pom jaxrs-server jaxrs-client org.apache.servicecomb solution-basic org.apache.servicecomb registry-service-center org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb swagger-generator-spring-data org.springframework.data spring-data-commons org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-local-registry/README.md ================================================ This demo is an integration test for testing local registration an discovery. Local registration and discovery is a mechanism that do not using Service Center, `Microservice` and `MicroserviceInstance` are constructed from configuration file. ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/pom.xml ================================================ 4.0.0 demo-local-registry-client Java Chassis::Demo::Local Registry client org.apache.servicecomb.demo demo-local-registry 3.4.0-SNAPSHOT org.apache.servicecomb.demo.localRegistryClient.LocalRegistryClientApplication org.apache.servicecomb.demo demo-schema docker demo-local-registry-server io.fabric8 docker-maven-plugin ${demo.service.name}:${project.version} ${demo.service.name} /maven/maven/${demo.service.name}-${project.version}.jar ServiceComb is ready 8080 8080:8080 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/CodeFirstService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryClient; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; @Path("/register/url/codeFirst") @Produces("application/json") public interface CodeFirstService { @GET @Path("getName") String getName(@QueryParam("name") String name); } ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryClient; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class LocalRegistryClientApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(LocalRegistryClientApplication.class) .web(WebApplicationType.SERVLET).build() .run(args); registerSchema(); runTest(); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { throw new IllegalStateException("tests failed"); } } private static void registerSchema() { SCBEngine.getInstance().getOpenAPIRegistryManager().registerOpenAPI("demo-local-registry", "demo-local-registry-server", "CodeFirstEndpoint", CodeFirstService.class); SCBEngine.getInstance().getOpenAPIRegistryManager().registerOpenAPI("demo-local-registry", "demo-local-registry-server-bean", "CodeFirstEndpoint", CodeFirstService.class); } private static void runTest() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("demo-local-registry-server"); } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryServerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryClient; import java.util.List; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class LocalRegistryServerTest implements CategorizedTestCase { // demo-local-registry-server-bean use yaml to register service, and register schema by // local file loading @RpcReference(microserviceName = "demo-local-registry-server", schemaId = "CodeFirstEndpoint") private CodeFirstService codeFirstService; @RpcReference(microserviceName = "demo-local-registry-server", schemaId = "ServerEndpoint") private ServerService serverService; // demo-local-registry-server-bean use bean to register service, and register schema part by // local file loading, part by bean class @RpcReference(microserviceName = "demo-local-registry-server-bean", schemaId = "CodeFirstEndpoint") private CodeFirstService codeFirstServiceBean; @RpcReference(microserviceName = "demo-local-registry-server-bean", schemaId = "ServerEndpoint") private ServerService serverServiceBean; // demo-local-registry-server-bean2 use bean to register service and schema @RpcReference(microserviceName = "demo-local-registry-server-bean2", schemaId = "CodeFirstEndpoint2") private CodeFirstService codeFirstServiceBean2; @RpcReference(microserviceName = "demo-local-registry-server-bean2", schemaId = "ServerEndpoint") private ServerService serverServiceBean2; private DiscoveryManager discoveryManager; @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Override public void testRestTransport() throws Exception { testServerGetName(); testCodeFirstGetName(); testGetAllMicroservice(); } private void testGetAllMicroservice() { List microserviceList = discoveryManager .findServiceInstances("demo-local-registry", "demo-local-registry-client"); TestMgr.check(1, microserviceList.size()); microserviceList = discoveryManager .findServiceInstances("demo-local-registry", "demo-local-registry-server"); TestMgr.check(1, microserviceList.size()); microserviceList = discoveryManager .findServiceInstances("demo-local-registry", "demo-local-registry-server-bean"); TestMgr.check(1, microserviceList.size()); microserviceList = discoveryManager .findServiceInstances("demo-local-registry", "demo-local-registry-server-bean2"); TestMgr.check(1, microserviceList.size()); } private void testCodeFirstGetName() { TestMgr.check("2", codeFirstService.getName("2")); TestMgr.check("2", codeFirstServiceBean.getName("2")); TestMgr.check("2", codeFirstServiceBean2.getName("2")); } private void testServerGetName() { RestOperations template = RestTemplateBuilder.create(); TestMgr.check("2", template .getForObject("cse://demo-local-registry-server/register/url/prefix/getName?name=2", String.class)); TestMgr.check("2", template .getForObject("cse://demo-local-registry-server-bean/register/url/prefix/getName?name=2", String.class)); TestMgr.check("2", template .getForObject("cse://demo-local-registry-server-bean2/register/url/prefix/getName?name=2", String.class)); TestMgr.check("2", serverService.getName("2")); TestMgr.check("2", serverServiceBean.getName("2")); TestMgr.check("2", serverServiceBean2.getName("2")); } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/RegistryBeansConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryClient; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @SuppressWarnings("unused") public class RegistryBeansConfiguration { @Bean public RegistryBean demoLocalRegistryServerBean() { List endpoints = new ArrayList<>(); endpoints.add("rest://localhost:8080"); List instances = new ArrayList<>(); instances.add(new Instance().setEndpoints(endpoints)); return new RegistryBean() .setServiceName("demo-local-registry-server-bean") .setId("002") .setVersion("0.0.3") .setAppId("demo-local-registry") .addSchemaId("ServerEndpoint") .addSchemaInterface("CodeFirstEndpoint", CodeFirstService.class) .setInstances(new Instances().setInstances(instances)); } @Bean public RegistryBean demoLocalRegistryServerBean2() { List endpoints = new ArrayList<>(); endpoints.add("rest://localhost:8080"); List instances = new ArrayList<>(); instances.add(new Instance().setEndpoints(endpoints)); return new RegistryBean() .setServiceName("demo-local-registry-server-bean2") .setId("003") .setVersion("0.0.3") .setAppId("demo-local-registry") .addSchemaId("ServerEndpoint") .addSchemaInterface("CodeFirstEndpoint2", CodeFirstService.class) .setInstances(new Instances().setInstances(instances)); } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/ServerService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryClient; public interface ServerService { String getName(String name); } ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8082 servicecomb: service: application: demo-local-registry name: demo-local-registry-client version: 0.0.1 ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint version: 1.0.0 servers: - url: /register/url/prefix paths: /getName: get: operationId: getName parameters: - name: name in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint version: 1.0.0 servers: - url: /register/url/prefix paths: /getName: get: operationId: getName parameters: - name: name in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint version: 1.0.0 servers: - url: /register/url/prefix paths: /getName: get: operationId: getName parameters: - name: name in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- demo-local-registry-server: - id: "001" version: "1.0" appid: demo-local-registry schemaIds: - ServerEndpoint - CodeFirstEndpoint instances: - endpoints: - rest://localhost:8080 ================================================ FILE: demo/demo-local-registry/demo-local-registry-client/src/test/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryClient; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class LocalRegistryIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { LocalRegistryClientApplication.main(new String[0]); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/pom.xml ================================================ 4.0.0 demo-local-registry-server Java Chassis::Demo::Local Registry Server org.apache.servicecomb.demo demo-local-registry 3.4.0-SNAPSHOT org.apache.servicecomb.demo.localRegistryServer.LocalRegistryServerApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryServer; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "CodeFirstEndpoint") @RequestMapping(path = "/register/url/codeFirst", produces = MediaType.APPLICATION_JSON) public class CodeFirstEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/LocalRegistryServerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryServer; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class LocalRegistryServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(LocalRegistryServerApplication.class).web(WebApplicationType.SERVLET).build() .run(args); SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); invoker.latch.await(10, TimeUnit.SECONDS); TestMgr.check(invoker.result, "hello"); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { System.exit(1); } } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/SelfServiceInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryServer; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component("SelfServiceInvoker") public class SelfServiceInvoker implements BootListener { interface IServerEndpoint { String getName(String name); } @RpcReference(microserviceName = "demo-local-registry-server", schemaId = "ServerEndpoint") IServerEndpoint endpoint; public CountDownLatch latch = new CountDownLatch(1); public String result = ""; public void onAfterRegistry(BootEvent event) { result = endpoint.getName("hello"); latch.countDown(); } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.localRegistryServer; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8080 # java-chassis configurations servicecomb: service: application: demo-local-registry name: demo-local-registry-server version: 0.0.1 rest: address: 0.0.0.0:8080 ================================================ FILE: demo/demo-local-registry/demo-local-registry-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-local-registry/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-local-registry Java Chassis::Demo::Local Registry pom demo-local-registry-server demo-local-registry-client org.apache.servicecomb solution-basic org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb registry-local org.apache.servicecomb foundation-test-scaffolding compile org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-multi-registries/README.md ================================================ # About * This demo enables both local-registry and sc-registry. * This demo using local-registry to call service center api. ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/pom.xml ================================================ 4.0.0 demo-multi-registries-client Java Chassis::Demo::Multi Registries client org.apache.servicecomb.demo demo-multi-registries 3.4.0-SNAPSHOT org.apache.servicecomb.demo.registry.MultiRegistriesClientApplication org.apache.servicecomb.demo demo-schema docker demo-multi-registries-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; public interface IServerEndpoint { String getName(String name); } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServiceCenterEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; public interface IServiceCenterEndpoint { // java name can not be `x-domain-name`, so interfaces define all parameters. @GetMapping(path = "/instances") Object getInstances(@RequestParam(name = "appId") String appId, @RequestParam(name = "serviceName") String serviceName, @RequestParam(name = "global") String global, @RequestParam(name = "version") String version, @RequestHeader(name = "x-domain-name") String domain); } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.stereotype.Component; @SpringBootApplication @Component public class MultiRegistriesClientApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(MultiRegistriesClientApplication.class) .web(WebApplicationType.SERVLET).build().run(args); runTest(); } public static void runTest() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("demo-multi-registries-server"); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { throw new IllegalStateException("tests failed"); } } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerTestCase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import java.util.List; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class MultiRegistriesServerTestCase implements CategorizedTestCase { RestOperations template = RestTemplateBuilder.create(); private DiscoveryManager discoveryManager; @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Override public void testRestTransport() throws Exception { testServerGetName(); testGetAllMicroservice(); } private void testGetAllMicroservice() { List microserviceList = discoveryManager .findServiceInstances("demo-multi-registries", "demo-multi-registries-client"); TestMgr.check(2, microserviceList.size()); // both local and sc has one. microserviceList = discoveryManager .findServiceInstances("demo-multi-registries", "demo-multi-registries-server"); TestMgr.check(1, microserviceList.size()); microserviceList = discoveryManager .findServiceInstances("demo-multi-registries", "thirdParty-service-center"); TestMgr.check(1, microserviceList.size()); microserviceList = discoveryManager .findServiceInstances("demo-multi-registries", "thirdParty-no-schema-server"); TestMgr.check(1, microserviceList.size()); } private void testServerGetName() { // invoke demo-multi-registries-server TestMgr.check("2", template .getForObject("cse://demo-multi-registries-server/register/url/prefix/getName?name=2", String.class)); } @Override public void testHighwayTransport() throws Exception { } @Override public void testAllTransport() throws Exception { } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/SchemaDiscoveryTestCase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import org.apache.servicecomb.demo.CategorizedTestCase; import org.springframework.stereotype.Component; @Component public class SchemaDiscoveryTestCase implements CategorizedTestCase { // Do not support this feature any more since 3.0.0 // @RpcReference(microserviceName = "thirdParty-no-schema-server", schemaId = "ServerEndpoint") // IServerEndpoint serverEndpoint; @Override public void testRestTransport() throws Exception { // invoke thirdParty-no-schema-server(mocked by demo-multi-registries-server) // TestMgr.check("hello", serverEndpoint.getName("hello")); } @Override public void testHighwayTransport() throws Exception { } @Override public void testAllTransport() throws Exception { } @Override public String getMicroserviceName() { return "thirdParty-service-center"; } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; // do not know how to write schemas? code first and generate it. @RestSchema(schemaId = "ServiceCenterEndpoint") @RequestMapping(path = "/v4/default/registry", produces = MediaType.APPLICATION_JSON) public class ServiceCenterEndpoint implements IServiceCenterEndpoint { @GetMapping(path = "/instances") public Object getInstances(@RequestParam(name = "appId") String appId, @RequestParam(name = "serviceName") String serviceName, @RequestParam(name = "global") String global, @RequestParam(name = "version") String version, @RequestHeader(name = "x-domain-name") String domain) { return null; } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterTestCase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import java.util.List; import java.util.Map; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class ServiceCenterTestCase implements CategorizedTestCase { @RpcReference(microserviceName = "thirdParty-service-center", schemaId = "ServiceCenterEndpoint") IServiceCenterEndpoint serviceCenterEndpoint; @Override public void testRestTransport() throws Exception { // invoke service-center(3rd-parties) @SuppressWarnings("unchecked") Map> result = (Map>) serviceCenterEndpoint.getInstances( "demo-multi-registries", "demo-multi-registries-server", "true", "0.0.2", "default"); TestMgr.check(result.get("instances").size(), 1); } @Override public void testHighwayTransport() throws Exception { } @Override public void testAllTransport() throws Exception { } @Override public String getMicroserviceName() { return "thirdParty-service-center"; } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8082 servicecomb: service: application: demo-multi-registries name: demo-multi-registries-client version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 demo-multi-registries: thirdParty-service-center: enabled: false local: demo-multi-registries: demo-multi-registries-server: enabled: false ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the License); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an AS IS BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint version: 1.0.0 servers: - url: /v4/default/registry paths: /instances: get: operationId: getInstances parameters: - name: appId in: query required: true schema: type: string - name: serviceName in: query required: true schema: type: string - name: global in: query required: true schema: type: string - name: version in: query required: true schema: type: string - name: x-domain-name in: header required: true schema: type: string responses: 200: description: response of 200 content: application/json: schema: type: object components: schemas: {} ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- thirdParty-service-center: - id: "001" version: "4.0.0" appid: demo-multi-registries schemaIds: - ServiceCenterEndpoint instances: - endpoints: - rest://localhost:30100 thirdParty-no-schema-server: - id: "002" version: "4.0.0" appid: demo-multi-registries schemaIds: - ServerEndpoint # not in local , discovery from demo-multi-registries-server. Not supported since 3.0.0 instances: - endpoints: - rest://localhost:8080 # actually will invoke to demo-multi-registries-server ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-client/src/test/java/org/apache/servicecomb/demo/registry/MultiRegistriesIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class MultiRegistriesIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { MultiRegistriesClientApplication.main(new String[0]); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-server/pom.xml ================================================ 4.0.0 demo-multi-registries-server Java Chassis::Demo::Multi Registries Server org.apache.servicecomb.demo demo-multi-registries 3.4.0-SNAPSHOT org.apache.servicecomb.demo.registry.MultiRegistriesServerApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class MultiRegistriesServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(MultiRegistriesServerApplication.class) .web(WebApplicationType.SERVLET).build().run(args); SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); invoker.latch.await(10, TimeUnit.SECONDS); TestMgr.check(invoker.result, "hello"); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { System.exit(1); } } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/SelfServiceInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component("SelfServiceInvoker") public class SelfServiceInvoker implements BootListener { interface IServerEndpoint { String getName(String name); } @RpcReference(microserviceName = "demo-multi-registries-server", schemaId = "ServerEndpoint") IServerEndpoint endpoint; public CountDownLatch latch = new CountDownLatch(1); public String result = ""; public void onAfterRegistry(BootEvent event) { result = endpoint.getName("hello"); latch.countDown(); } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.registry; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8080 servicecomb: service: application: demo-multi-registries name: demo-multi-registries-server version: 0.0.2 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 ================================================ FILE: demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multi-registries/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-multi-registries Java Chassis::Demo::Multi Registry pom demo-multi-registries-server demo-multi-registries-client org.apache.servicecomb solution-basic org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb registry-local org.apache.servicecomb registry-service-center org.apache.servicecomb foundation-test-scaffolding compile org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml ================================================ 4.0.0 demo-multi-service-center-client Java Chassis::Demo::Multi Service Centers::Client org.apache.servicecomb.demo demo-multi-service-center 3.4.0-SNAPSHOT org.apache.servicecomb.demo.multiServiceCenterClient.Application org.apache.servicecomb service-center-client org.apache.servicecomb.demo demo-schema docker demo-multi-registries-server io.fabric8 docker-maven-plugin servicecomb/service-center service30100 alias server is ready 30100 30100:30100 servicecomb/service-center service40100 alias server is ready 30100 40100:30100 demo-multi-service-center-servera:${project.version} demo-multi-service-center-servera alias -Dservicecomb.registry.sc.address=http://service30100:30100 /maven/maven/demo-multi-service-center-servera-${project.version}.jar service30100:service30100 ServiceComb is ready 8080 8080:8080 demo-multi-service-center-serverb:${project.version} demo-multi-service-center-serverb alias -Dservicecomb.registry.sc.address=http://service40100:30100 /maven/maven/demo-multi-service-center-serverb-${project.version}.jar service40100:service40100 ServiceComb is ready 8082 8082:8082 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/Application.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.stereotype.Component; @SpringBootApplication @Component public class Application { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(Application.class) .web(WebApplicationType.NONE).build().run(args); runTest(); } public static void runTest() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("demo-multi-service-center-serverA"); testTransportThreads(); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { throw new IllegalStateException("tests failed"); } } private static void testTransportThreads() throws Exception { Set threadSet = Thread.getAllStackTraces().keySet(); List expectedThread = new ArrayList<>(); threadSet.forEach(thread -> { if (thread.getName().startsWith("transport-vert.x-eventloop-thread")) { expectedThread.add(thread.getName()); } }); // After SSL improvements https://github.com/eclipse-vertx/vert.x/pull/4468 introduced in 4.3.4 // SSL helper will use `internal-blocking` thread init ssl engine // transport-vert.x-eventloop-thread-0 // transport-vert.x-eventloop-thread-1 // transport-vert.x-eventloop-thread-2 // transport-vert.x-eventloop-thread-3 // transport-vert.x-eventloop-thread-4 // transport-vert.x-eventloop-thread-5 // transport-vert.x-internal-blocking-0 // transport-vert.x-internal-blocking-1 TestMgr.check(6, expectedThread.size()); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IConfigurationEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import java.util.List; public interface IConfigurationEndpoint { String getValue(String key, int type); List getValueList(String key, int type); } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; public interface IServerEndpoint { String getName(String name); } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; import org.apache.servicecomb.http.client.auth.DefaultRequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; import org.apache.servicecomb.service.center.client.RegistrationEvents; import org.apache.servicecomb.service.center.client.RegistrationEvents.HeartBeatEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceRegistrationEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.SchemaRegistrationEvent; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery; import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery.SubscriptionKey; import org.apache.servicecomb.service.center.client.ServiceCenterRegistration; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.apache.servicecomb.service.center.client.model.ServiceCenterConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import com.google.common.base.Charsets; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.common.hash.Hashing; @Component public class RegistryClientTest implements CategorizedTestCase { private List events = new ArrayList<>(); private CountDownLatch registrationCounter = new CountDownLatch(1); // auto test only tests 'hasRegistered=false', can run this client many times to test 'hasRegistered=true' private boolean hasRegistered = true; private final Environment environment; @Autowired public RegistryClientTest(Environment environment) { this.environment = environment; } @Override public void testRestTransport() throws Exception { ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager("default", Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(false); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(addressManager, sslProperties, new DefaultRequestAuthHeaderProvider(), "default", null, environment); EventBus eventBus = new SimpleEventBus(); ServiceCenterConfiguration serviceCenterConfiguration = new ServiceCenterConfiguration(); ServiceCenterRegistration serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, serviceCenterConfiguration, eventBus); Microservice microservice = new Microservice(); microservice.setAppId("app_registry"); microservice.setServiceName("name_registry"); microservice.setVersion("1.0.0"); microservice.setEnvironment("development"); List schemas = new ArrayList<>(); schemas.add("SchemaA"); schemas.add("SchemaB"); microservice.setSchemas(schemas); MicroserviceInstance microserviceInstance = new MicroserviceInstance(); microserviceInstance.setHostName("host_registry"); List endpoints = new ArrayList<>(); endpoints.add("rest://127.0.0.1/"); microserviceInstance.setEndpoints(endpoints); List schemaInfos = new ArrayList<>(); SchemaInfo schemaA = new SchemaInfo(); schemaA.setSchemaId("SchemaA"); schemaA.setSchema("schema contents in any format"); schemaA.setSummary( Hashing.sha256().newHasher().putString("schema contents in any format".toString(), Charsets.UTF_8).hash() .toString()); schemaInfos.add(schemaA); SchemaInfo schemaB = new SchemaInfo(); schemaB.setSchemaId("SchemaA"); schemaB.setSchema("schema contents in any format"); schemaB.setSummary( Hashing.sha256().newHasher().putString("schema contents in any format".toString(), Charsets.UTF_8).hash() .toString()); schemaInfos.add(schemaB); serviceCenterRegistration.setMicroservice(microservice); serviceCenterRegistration.setMicroserviceInstance(microserviceInstance); serviceCenterRegistration.setSchemaInfos(schemaInfos); eventBus.register(this); serviceCenterRegistration.startRegistration(); registrationCounter.await(30000, TimeUnit.MILLISECONDS); if (hasRegistered) { TestMgr.check(events.size() >= 3, true); TestMgr.check(events.get(0).isSuccess(), true); TestMgr.check(events.get(0) instanceof MicroserviceRegistrationEvent, true); TestMgr.check(events.get(1).isSuccess(), true); TestMgr.check(events.get(1) instanceof MicroserviceInstanceRegistrationEvent, true); TestMgr.check(events.get(2).isSuccess(), true); TestMgr.check(events.get(2) instanceof HeartBeatEvent, true); } else { TestMgr.check(events.size() >= 4, true); TestMgr.check(events.get(0).isSuccess(), true); TestMgr.check(events.get(0) instanceof MicroserviceRegistrationEvent, true); TestMgr.check(events.get(1).isSuccess(), true); TestMgr.check(events.get(1) instanceof SchemaRegistrationEvent, true); TestMgr.check(events.get(2).isSuccess(), true); TestMgr.check(events.get(2) instanceof MicroserviceInstanceRegistrationEvent, true); TestMgr.check(events.get(3).isSuccess(), true); TestMgr.check(events.get(3) instanceof HeartBeatEvent, true); } ServiceCenterDiscovery discovery = new ServiceCenterDiscovery(serviceCenterClient, eventBus); discovery.updateMyselfServiceId(microservice.getServiceId()); discovery.startDiscovery(); SubscriptionKey subscriptionKey = new SubscriptionKey(microservice.getAppId(), microservice.getServiceName()); discovery.registerIfNotPresent(subscriptionKey); List instances = discovery.getInstanceCache(subscriptionKey); TestMgr.check(instances != null, true); TestMgr.check(instances.size(), 1); discovery.stop(); serviceCenterRegistration.stop(); serviceCenterClient.deleteMicroserviceInstance(microservice.getServiceId(), microserviceInstance.getInstanceId()); } @Subscribe public void onMicroserviceRegistrationEvent(MicroserviceRegistrationEvent event) { events.add(event); } @Subscribe public void onSchemaRegistrationEvent(SchemaRegistrationEvent event) { events.add(event); hasRegistered = false; } @Subscribe public void onMicroserviceInstanceRegistrationEvent(MicroserviceInstanceRegistrationEvent event) { events.add(event); } @Subscribe public void onHeartBeatEvent(HeartBeatEvent event) { events.add(event); registrationCounter.countDown(); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import java.util.List; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.registry.sc.SCClientUtils; import org.apache.servicecomb.registry.sc.SCConfigurationProperties; import org.apache.servicecomb.registry.sc.SCConst; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterWatch; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class SC2Configuration { @Bean @ConfigurationProperties(prefix = SCConst.SC_REGISTRY_PREFIX + "2") public SCConfigurationProperties scConfigurationProperties2() { return new SCConfigurationProperties(); } @Bean public ServiceCenterWatch serviceCenterWatch2( @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, List authHeaderProviders, Environment environment) { return SCClientUtils.serviceCenterWatch(scConfigurationProperties2, authHeaderProviders, environment); } @Bean public ServiceCenterClient serviceCenterClient2( @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, Environment environment) { return SCClientUtils.serviceCenterClient(scConfigurationProperties2, environment); } @Bean public SC2Discovery sc2Discovery( @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, @Qualifier("serviceCenterClient2") ServiceCenterClient serviceCenterClient2) { return new SC2Discovery(scConfigurationProperties2, serviceCenterClient2); } @Bean public SC2Registration sc2Registration( @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, @Qualifier("serviceCenterClient2") ServiceCenterClient serviceCenterClient2, @Qualifier("serviceCenterWatch2") ServiceCenterWatch serviceCenterWatch2) { return new SC2Registration(scConfigurationProperties2, serviceCenterClient2, serviceCenterWatch2); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Discovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import org.apache.servicecomb.registry.sc.SCConfigurationProperties; import org.apache.servicecomb.registry.sc.SCConst; import org.apache.servicecomb.registry.sc.SCDiscovery; import org.apache.servicecomb.service.center.client.ServiceCenterClient; public class SC2Discovery extends SCDiscovery { public SC2Discovery(SCConfigurationProperties configurationProperties, ServiceCenterClient serviceCenterClient) { super(configurationProperties, serviceCenterClient); } @Override public String name() { return SCConst.SC_REGISTRY_NAME + "2"; } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Registration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import org.apache.servicecomb.registry.sc.SCConfigurationProperties; import org.apache.servicecomb.registry.sc.SCConst; import org.apache.servicecomb.registry.sc.SCRegistration; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterWatch; public class SC2Registration extends SCRegistration { public SC2Registration(SCConfigurationProperties configurationProperties, ServiceCenterClient serviceCenterClient, ServiceCenterWatch serviceCenterWatch) { super(configurationProperties, serviceCenterClient, serviceCenterWatch); } @Override public String name() { return SCConst.SC_REGISTRY_NAME + 2; } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerATest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class ServerATest implements CategorizedTestCase { @RpcReference(microserviceName = "demo-multi-service-center-serverA", schemaId = "ServerEndpoint") private IServerEndpoint serverEndpoint; @Override public void testRestTransport() throws Exception { TestMgr.check("hello", serverEndpoint.getName("hello")); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import java.util.Arrays; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class ServerBTest implements CategorizedTestCase { @RpcReference(microserviceName = "demo-multi-service-center-serverB", schemaId = "ServerEndpoint") private IServerEndpoint serverEndpoint; @RpcReference(microserviceName = "demo-multi-service-center-serverB", schemaId = "ConfigurationEndpoint") private IConfigurationEndpoint configurationEndpoint; @Override public void testRestTransport() throws Exception { TestMgr.check("hello", serverEndpoint.getName("hello")); TestMgr.check("key1-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key1", 1)); TestMgr.check("key1-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key1", 2)); TestMgr.check("key1-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key1", 3)); TestMgr.check("key2-override", configurationEndpoint.getValue("demo.multi.service.center.serverB.key2", 1)); TestMgr.check("key2-override", configurationEndpoint.getValue("demo.multi.service.center.serverB.key2", 2)); TestMgr.check("key2-override", configurationEndpoint.getValue("demo.multi.service.center.serverB.key2", 3)); TestMgr.check("key3", configurationEndpoint.getValue("demo.multi.service.center.serverB.key3", 1)); TestMgr.check("key3", configurationEndpoint.getValue("demo.multi.service.center.serverB.key3", 2)); TestMgr.check("key3", configurationEndpoint.getValue("demo.multi.service.center.serverB.key3", 3)); TestMgr.check("key4-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key4", 1)); TestMgr.check("key4-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key4", 2)); TestMgr.check("key4-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key4", 3)); TestMgr.check("key5-high", configurationEndpoint.getValue("demo.multi.service.center.serverB.key5", 1)); TestMgr.check("key5-high", configurationEndpoint.getValue("demo.multi.service.center.serverB.key5", 2)); TestMgr.check("key5-high", configurationEndpoint.getValue("demo.multi.service.center.serverB.key5", 3)); TestMgr.check("key6", configurationEndpoint.getValue("demo.multi.service.center.serverB.key6", 1)); TestMgr.check("key6", configurationEndpoint.getValue("demo.multi.service.center.serverB.key6", 2)); TestMgr.check("key6", configurationEndpoint.getValue("demo.multi.service.center.serverB.key6", 3)); TestMgr.check(Arrays.asList("key71", "key72"), configurationEndpoint.getValueList("demo.multi.service.center.serverB.key7", 1)); TestMgr.check(Arrays.asList("key71", "key72"), configurationEndpoint.getValueList("demo.multi.service.center.serverB.key7", 2)); } @Override public String getMicroserviceName() { return "demo-multi-service-center-serverB"; } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-multi-service-center name: demo-multi-service-center-client version: 0.0.2 registry: sc: address: http://127.0.0.1:30100 sc2: address: http://127.0.0.1:40100 transport: eventloop: size: 6 ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-client/src/test/java/org/apache/servicecomb/demo/multiServiceCenterClient/MultiServiceCenterIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterClient; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class MultiServiceCenterIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { Application.main(new String[0]); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml ================================================ 4.0.0 demo-multi-service-center-servera Java Chassis::Demo::Multi Service Centers::ServerA org.apache.servicecomb.demo demo-multi-service-center 3.4.0-SNAPSHOT org.apache.servicecomb.demo.multiServiceCenter.ServerApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenter; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(ServerApplication.class) .web(WebApplicationType.SERVLET).build().run(args); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenter; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8080 servicecomb: service: application: demo-multi-service-center name: demo-multi-service-center-serverA version: 0.0.2 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml ================================================ 4.0.0 demo-multi-service-center-serverb Java Chassis::Demo::Multi Service Centers::ServerB org.apache.servicecomb.demo demo-multi-service-center 3.4.0-SNAPSHOT org.apache.servicecomb.demo.multiServiceCenterServerB.ServerApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ConfigurationEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterServerB; import java.util.List; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ConfigurationEndpoint") @RequestMapping(path = "/register/url/config", produces = MediaType.APPLICATION_JSON) public class ConfigurationEndpoint { private static final Logger LOGGER = LoggerFactory.getLogger(ServerEndpoint.class); private Environment environment; @Autowired public ConfigurationEndpoint(Environment environment) { this.environment = environment; } @Value("${demo.multi.service.center.serverB.key1}") private String key1; @Value("${demo.multi.service.center.serverB.key2}") private String key2; @Value("${demo.multi.service.center.serverB.key3}") private String key3; @Value("${demo.multi.service.center.serverB.key4}") private String key4; @Value("${demo.multi.service.center.serverB.key5}") private String key5; @Value("${demo.multi.service.center.serverB.key6}") private String key6; @Value("${demo.multi.service.center.serverB.key7}") private List key7; @GetMapping(path = "/config") public String getValue(@RequestParam(name = "key") String key, @RequestParam(name = "type") int type) { if (type == 1) { return environment.getProperty(key); } else if (type == 2) { return environment.getProperty(key); } else { switch (key) { case "demo.multi.service.center.serverB.key1": return key1; case "demo.multi.service.center.serverB.key2": return key2; case "demo.multi.service.center.serverB.key3": return key3; case "demo.multi.service.center.serverB.key4": return key4; case "demo.multi.service.center.serverB.key5": return key5; case "demo.multi.service.center.serverB.key6": return key6; default: return null; } } } @SuppressWarnings("unchecked") @GetMapping(path = "/configList") public List getValueList(@RequestParam(name = "key") String key, @RequestParam(name = "type") int type) { if (type == 1) { return environment.getProperty(key, List.class); } else { switch (key) { case "demo.multi.service.center.serverB.key7": return key7; default: return null; } } } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterServerB; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(ServerApplication.class) .web(WebApplicationType.SERVLET).build().run(args); } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiServiceCenterServerB; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.properties ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- demo.multi.service.center.serverB.actual.key6=key6 demo.multi.service.center.serverB.actual.key7.1=key71 demo.multi.service.center.serverB.actual.key7.2=key72 ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8082 servicecomb: service: application: demo-multi-service-center name: demo-multi-service-center-serverB version: 0.0.2 registry: sc: address: http://127.0.0.1:40100 rest: address: 0.0.0.0:8082 demo.multi.service.center.serverB: key1: key1-boot key4: key4-boot key5: key5 ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/mapping.yaml ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # demo.multi.service.center.serverB.actual.key5: - demo.multi.service.center.serverB.key5 ================================================ FILE: demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: 1 demo.multi.service.center.serverB: key1: key1-override key2: key2-override key5: key5 actual: key5: key5-high key6: ${demo.multi.service.center.serverB.actual.key6} key7: - ${demo.multi.service.center.serverB.actual.key7.1} - ${demo.multi.service.center.serverB.actual.key7.2} ================================================ FILE: demo/demo-multi-service-center/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-multi-service-center Java Chassis::Demo::Multi Service Centers pom demo-multi-service-center-serverA demo-multi-service-center-serverB demo-multi-service-center-client org.apache.servicecomb solution-basic org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb registry-service-center org.apache.servicecomb foundation-test-scaffolding compile org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-multiple/a-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-multiple 3.4.0-SNAPSHOT a-client Java Chassis::Demo::Multiple::A client org.apache.servicecomb.demo.multiple.a.client.AClientMain ================================================ FILE: demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.a.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class AClient { @RpcReference(microserviceName = "${a-server.name}", schemaId = "a-hello") private AIntf intf; public void run() { String result = intf.hello("serviceComb"); TestMgr.check("a hello serviceComb", result); } } ================================================ FILE: demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClientMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.a.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; public class AClientMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(AClientMain.class).web(WebApplicationType.NONE).run(args); AClient client = BeanUtils.getContext().getBean(AClient.class); client.run(); TestMgr.summary(); } } ================================================ FILE: demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.a.client; public interface AIntf { String hello(String name); } ================================================ FILE: demo/demo-multiple/a-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multiple/a-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- a-server.name: a-server servicecomb: service: application: multiple name: a-client version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 ================================================ FILE: demo/demo-multiple/a-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-multiple 3.4.0-SNAPSHOT a-server Java Chassis::Demo::Multiple::A server org.apache.servicecomb.demo.multiple.a.server.AServerMain ================================================ FILE: demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.a.server; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "a-hello") public class AImpl { public String hello(String name) { return "a hello " + name; } } ================================================ FILE: demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AServerMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.a.server; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; public class AServerMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(AServerMain.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-multiple/a-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multiple/a-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: multiple name: a-server version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 highway: address: 0.0.0.0:7070 ================================================ FILE: demo/demo-multiple/b-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-multiple 3.4.0-SNAPSHOT b-client Java Chassis::Demo::Multiple::B client org.apache.servicecomb.demo.multiple.b.client.BClientMain ================================================ FILE: demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.b.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class BClient { @RpcReference(microserviceName = "${b-server.name}", schemaId = "b-hello") private BIntf intf; public void run() { String result = intf.hello("serviceComb"); TestMgr.check("b hello serviceComb", result); } } ================================================ FILE: demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClientMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.b.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; public class BClientMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BClientMain.class).web(WebApplicationType.NONE).run(args); BClient client = BeanUtils.getContext().getBean(BClient.class); client.run(); TestMgr.summary(); } } ================================================ FILE: demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.b.client; public interface BIntf { String hello(String name); } ================================================ FILE: demo/demo-multiple/b-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multiple/b-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- b-server.name: b-server servicecomb: service: application: multiple name: b-client version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 ================================================ FILE: demo/demo-multiple/b-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-multiple 3.4.0-SNAPSHOT b-server Java Chassis::Demo::Multiple::B server org.apache.servicecomb.demo.multiple.b.server.BServerMain ================================================ FILE: demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.b.server; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "b-hello") public class BImpl { public String hello(String name) { return "b hello " + name; } } ================================================ FILE: demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BServerMain.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.b.server; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; public class BServerMain { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(BServerMain.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-multiple/b-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multiple/b-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: multiple name: b-server version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 highway: address: 0.0.0.0:7070 ================================================ FILE: demo/demo-multiple/multiple-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-multiple 3.4.0-SNAPSHOT multiple-client Java Chassis::Demo::Multiple::Client org.apache.servicecomb.demo a-client org.apache.servicecomb.demo b-client org.apache.servicecomb.demo.multiple.client.MultipleClient docker multiple-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multiple/multiple-client/src/main/java/org/apache/servicecomb/demo/multiple/client/MultipleClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.multiple.a.client.AClient; import org.apache.servicecomb.demo.multiple.b.client.BClient; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan(basePackages = {"org.apache.servicecomb.demo.multiple.a.client", "org.apache.servicecomb.demo.multiple.b.client"}) public class MultipleClient { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(MultipleClient.class).web(WebApplicationType.NONE).run(args); runTest(); } public static void runTest() { AClient aClient = BeanUtils.getContext().getBean(AClient.class); BClient bClient = BeanUtils.getContext().getBean(BClient.class); aClient.run(); bClient.run(); TestMgr.summary(); } } ================================================ FILE: demo/demo-multiple/multiple-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: 1000 a-server.name: merged-server b-server.name: merged-server servicecomb: service: application: multiple name: merged-client version: 0.0.1 ================================================ FILE: demo/demo-multiple/multiple-client/src/test/java/org/apache/servicecomb/demo/multiple/client/MultipleIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.client; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = MultipleClient.class) public class MultipleIT { @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { MultipleClient.runTest(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-multiple/multiple-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-multiple 3.4.0-SNAPSHOT multiple-server Java Chassis::Demo::Multiple::Server org.apache.servicecomb.demo a-server org.apache.servicecomb.demo b-server org.apache.servicecomb.demo.multiple.server.MultipleServer org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-multiple/multiple-server/src/main/java/org/apache/servicecomb/demo/multiple/server/MultipleServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiple.server; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan(basePackages = {"org.apache.servicecomb.demo.multiple.a.server", "org.apache.servicecomb.demo.multiple.b.server"}) public class MultipleServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(MultipleServer.class).web(WebApplicationType.NONE).run(args); } } ================================================ FILE: demo/demo-multiple/multiple-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: 1000 servicecomb: service: application: multiple name: merged-server version: 0.0.1 ================================================ FILE: demo/demo-multiple/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-multiple Java Chassis::Demo::Multiple pom a-server a-client b-server b-client multiple-server multiple-client org.apache.servicecomb.demo a-server ${project.version} org.apache.servicecomb.demo a-client ${project.version} org.apache.servicecomb.demo b-server ${project.version} org.apache.servicecomb.demo b-client ${project.version} org.apache.servicecomb registry-service-center org.apache.servicecomb.demo demo-schema org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-nacos/README.md ================================================ # Notice This integration tests is designed for Nacos registry and configuration. And extra test cases include: * Test cases related to JAX-RS annotations that demo-jaxrs can not cover. ================================================ FILE: demo/demo-nacos/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-nacos 3.4.0-SNAPSHOT nacos-consumer Java Chassis::Demo::NACOS::CONSUMER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-nacos org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ConsumerController") @RequestMapping(path = "/") public class ConsumerController { @RpcReference(schemaId = "ProviderController", microserviceName = "provider") private ProviderService providerService; // consumer service which delegate the implementation to provider service. @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } @GetMapping("/authIncludePath") public String authIncludePath() { return providerService.authIncludePath(); } } ================================================ FILE: demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchema.class) public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchema { @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") private IHeaderParamWithListSchema provider; @Override public String headerListDefault(List headerList) { return provider.headerListDefault(headerList); } @Override public String headerListCSV(List headerList) { return provider.headerListCSV(headerList); } @Override public String headerListMULTI(List headerList) { return provider.headerListMULTI(headerList); } @Override public String headerListSSV(List headerList) { return provider.headerListSSV(headerList); } @Override public String headerListPIPES(List headerList) { return provider.headerListPIPES(headerList); } } ================================================ FILE: demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface ProviderService { String sayHello(String name); String authIncludePath(); } ================================================ FILE: demo/demo-nacos/consumer/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-nacos version: 0.0.1 name: consumer registry: nacos: serverAddr: ${PAAS_CSE_NACOS_ENDPOINT:http://127.0.0.1:8848} metadata: group: red rest: address: 0.0.0.0:9092 publicKey: accessControl: enabled: true excludePathPatterns: '/*' ================================================ FILE: demo/demo-nacos/consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-nacos/gateway/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-nacos 3.4.0-SNAPSHOT nacos-gateway Java Chassis::Demo::NACOS::GATEWAY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb edge-core org.apache.servicecomb registry-nacos org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-nacos/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-nacos/gateway/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-nacos version: 0.0.1 name: gateway registry: nacos: enabled: true serverAddr: ${PAAS_CSE_NACOS_ENDPOINT:http://127.0.0.1:8848} rest: address: 0.0.0.0:9090?sslEnabled=false http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: consumer: prefixSegmentCount: 0 path: "/.*" microserviceName: consumer versionRule: 0.0.0+ ================================================ FILE: demo/demo-nacos/gateway/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-nacos/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-nacos Java Chassis::Demo::NACOS pom org.apache.servicecomb.demo demo-schema org.apache.servicecomb solution-basic org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-core org.apache.logging.log4j log4j-api provider consumer gateway test-client ================================================ FILE: demo/demo-nacos/provider/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-nacos 3.4.0-SNAPSHOT nacos-provider Java Chassis::Demo::NACOS::PROVIDER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-nacos org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchema.class) public class HeaderParamWithListSchema implements IHeaderParamWithListSchema { @Override public String headerListDefault(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListCSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListMULTI(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListSSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListPIPES(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } } ================================================ FILE: demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController { // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return "Hello " + name; } @GetMapping("/authIncludePath") public String authIncludePath() { return "success."; } } ================================================ FILE: demo/demo-nacos/provider/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations servicecomb: service: application: demo-nacos version: 0.0.1 name: provider registry: nacos: enabled: true serverAddr: ${PAAS_CSE_NACOS_ENDPOINT:http://127.0.0.1:8848} metadata: group: green rest: address: 0.0.0.0:9094 publicKey: accessControl: enabled: true includePathPatterns: '/authIncludePath' black: authPath: category: property propertyName: serviceName rule: consumer* ================================================ FILE: demo/demo-nacos/provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-nacos/test-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-nacos 3.4.0-SNAPSHOT nacos-test-client Java Chassis::Demo::NACOS::TEST-CLIENT jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-local docker io.fabric8 docker-maven-plugin nacos/nacos-server:v2.1.2-slim nacos-server alias standalone hostname Tomcat started on port 8848 nacos-server.port:8848 nacos-provider:${project.version} nacos-provider alias -Dservicecomb.registry.nacos.serverAddr=http://nacos-server:8848 /maven/maven/nacos-provider-${project.version}.jar nacos-server:nacos-server Nacos is ready 9094 9094:9094 nacos-consumer:${project.version} nacos-consumer alias -Dservicecomb.registry.nacos.serverAddr=http://nacos-server:8848 /maven/maven/nacos-consumer-${project.version}.jar nacos-server:nacos-server Nacos is ready 9092 9092:9092 nacos-gateway:${project.version} nacos-gateway alias -Dservicecomb.registry.nacos.serverAddr=http://nacos-server:8848 /maven/maven/nacos-gateway-${project.version}.jar nacos-server:nacos-server Nacos is ready 9090 9090:9090 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface Config { String GATEWAY_URL = "http://localhost:9090"; } ================================================ FILE: demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HeaderParamWithListSchemaIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHeaderListDefault(); testHeaderListMulti(); testHeaderListCSV(); testHeaderListSSV(); testHeaderListPipes(); } // default to multi private void testHeaderListDefault() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListPipes() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a|b|c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListSSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a b c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListCSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a,b,c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); headers.add("headerList", "a, b, c"); entity = new HttpEntity<>(headers); result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListMulti() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } } ================================================ FILE: demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHelloWorld(); testAuthIncludePath(); } private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); TestMgr.check("Hello World", result); } private void testAuthIncludePath() { String result = ""; try { result = template.getForObject(Config.GATEWAY_URL + "/authIncludePath", String.class); } catch (Exception e) { result = "failed"; } TestMgr.check("failed", result); } } ================================================ FILE: demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); } } ================================================ FILE: demo/demo-nacos/test-client/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-nacos name: test-client version: 0.0.1 rest: address: 0.0.0.0:9097 # should be same with server.port to use web container ================================================ FILE: demo/demo-nacos/test-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-nacos/test-client/src/test/java/org/apache/servicecomb/samples/NocasIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = TestClientApplication.class) public class NocasIT { @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { TestClientApplication.run(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-pojo/pojo-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-pojo 3.4.0-SNAPSHOT pojo-client Java Chassis::Demo::POJO::Client org.apache.servicecomb.demo demo-schema org.apache.servicecomb metrics-core org.apache.servicecomb foundation-test-scaffolding compile org.apache.servicecomb.demo.pojo.client.PojoClient docker pojo-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import org.apache.servicecomb.demo.server.Test; import org.apache.servicecomb.provider.pojo.RpcReference; public class BeanRpcTest { @RpcReference(microserviceName = "pojo", schemaId = "server") private Test test; public BeanRpcTest() { System.out.println("init"); } public void init() { new Thread(() -> { while (true) { try { System.out.println("XXXXXXXXXXXXXXXXXXXXXXXX" + test.getTestString(null)); break; } catch (Exception e) { e.printStackTrace(); try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } continue; } catch (Throwable e) { System.out.println("XXXXXXXXXXXXXXXXXXXXXXXX: teset case error"); e.printStackTrace(); System.exit(0); } } }).start(); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/ClientInterfaceForServerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import org.apache.servicecomb.demo.server.TestRequest; import org.apache.servicecomb.demo.server.User; public interface ClientInterfaceForServerTest { User splitParam(int nameNotIndex, User user); User wrapParam(TestRequest nameNotRequest); } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import javax.inject.Inject; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.CodeFirstPojoIntf; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.mapnull.ParseRequest; import org.apache.servicecomb.demo.mapnull.ParseResponse; import org.apache.servicecomb.demo.server.MapModel; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.stereotype.Component; import io.vertx.core.Vertx; @Component public class CodeFirstPojoClient implements CategorizedTestCase { @RpcReference(microserviceName = "pojo", schemaId = "org.apache.servicecomb.demo.CodeFirstPojoIntf") public CodeFirstPojoClientIntf codeFirstAnnotation; @RpcReference(microserviceName = "pojo") public CodeFirstPojoIntf codeFirstAnnotationEmptySchemaId; @Inject private CodeFirstPojoIntf codeFirstFromXml; @Override public void testRestTransport() throws Exception { testOnlyRest(codeFirstAnnotation); } @Override public void testAllTransport() throws Exception { testAll(codeFirstAnnotation); testAll(codeFirstAnnotationEmptySchemaId); testAll(codeFirstFromXml); } private void testOnlyRest(CodeFirstPojoIntf codeFirst) { testCodeFirstStrings(codeFirst); } private void testAll(CodeFirstPojoIntf codeFirst) { remoteCodeFirstPojo_testParseResponse(codeFirst); remoteCodeFirstPojo_testMapModel(codeFirst); remoteCodeFirstPojo_testMap(codeFirst); testCodeFirstUserMap(codeFirst); testCodeFirstUserArray(codeFirst); testCodeFirstStrings(codeFirst); testCodeFirstBytes(codeFirst); testCodeFirstAddDate(codeFirst); testCodeFirstAddString(codeFirst); testCodeFirstIsTrue(codeFirst); testCodeFirstSayHi2(codeFirst); testCodeFirstSayHi(codeFirst); testCodeFirstSaySomething(codeFirst); // testCodeFirstRawJsonString(template, cseUrlPrefix); testCodeFirstSayHello(codeFirst); testCodeFirstReduce(codeFirst); testCodeFirstCompletableFuture(codeFirst); } private void testCodeFirstCompletableFuture(CodeFirstPojoIntf codeFirst) { if (!CodeFirstPojoClientIntf.class.isInstance(codeFirst)) { return; } Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null, null); CountDownLatch latch = new CountDownLatch(1); // vertx.runOnContext in normal thread is not a good practice // here just a test, not care for this. vertx.runOnContext(V -> { InvocationContext context = new InvocationContext(); context.addContext("k", "v"); ContextUtils.setInvocationContext(context); ((CodeFirstPojoClientIntf) codeFirst).sayHiAsync("someone") .thenCompose(result -> { TestMgr.check("someone sayhi, context k: v", result); // new call need set invocation context implicitly or will not // inherit parent context ContextUtils.setInvocationContext(context); return ((CodeFirstPojoClientIntf) codeFirst).sayHiAsync("someone 1"); }) .whenComplete((r, e) -> { TestMgr.check("someone 1 sayhi, context k: v", r); latch.countDown(); }); ContextUtils.removeInvocationContext(); }); try { latch.await(); } catch (InterruptedException e) { throw new IllegalStateException(e); } } private void remoteCodeFirstPojo_testParseResponse(CodeFirstPojoIntf codeFirst) { ParseResponse r = codeFirst.parse(new ParseRequest()); TestMgr.check("", r.getMsgHeader().get("K16")); TestMgr.check("CMT", r.getMsgHeader().get("K14")); } private void remoteCodeFirstPojo_testMapModel(CodeFirstPojoIntf codeFirst) { MapModel model = new MapModel(); model.setName("hello"); Map userMap = new HashMap<>(); userMap.put("u1", "u1"); userMap.put("u2", null); model.setNames(userMap); MapModel result = codeFirst.testMapModel(model); TestMgr.check(result.getName(), "hello"); TestMgr.check(result.getNames().get("u1"), "u1"); TestMgr.check(result.getNames().get("u2"), null); model = new MapModel(); model.setName(null); userMap = new HashMap<>(); userMap.put("u1", "u1"); userMap.put("u2", null); model.setNames(userMap); result = codeFirst.testMapModel(model); TestMgr.check(result.getName(), null); TestMgr.check(result.getNames().get("u1"), "u1"); TestMgr.check(result.getNames().get("u2"), null); model = new MapModel(); model.setName(null); userMap = new HashMap<>(); userMap.put("u1", "u1"); userMap.put("u2", ""); model.setNames(userMap); result = codeFirst.testMapModel(model); TestMgr.check(result.getName(), null); TestMgr.check(result.getNames().get("u1"), "u1"); TestMgr.check(result.getNames().get("u2"), ""); } private void remoteCodeFirstPojo_testMap(CodeFirstPojoIntf codeFirst) { Map userMap = new HashMap<>(); userMap.put("u1", "u1"); userMap.put("u2", null); Map result = codeFirst.testMap(userMap); TestMgr.check(result.get("u1"), "u1"); TestMgr.check(result.get("u2"), null); userMap = new HashMap<>(); userMap.put("u1", "u1"); userMap.put("u2", "u2"); result = codeFirst.testMap(userMap); TestMgr.check(result.get("u1"), "u1"); TestMgr.check(result.get("u2"), "u2"); // test large data more than 20M // can not run the test case in CI , because will cause heap size limit // char[] data = new char[30 * 1024 * 1024]; // Arrays.fill(data, 'h'); // userMap = new HashMap<>(); // userMap.put("u1", "u1"); // userMap.put("u2", "u2"); // userMap.put("u3", new String(data)); // result = codeFirst.testMap(userMap); // // TestMgr.check(result.get("u1"), "u1"); // TestMgr.check(result.get("u2"), "u2"); // TestMgr.check(result.get("u3"), new String(data)); } private void testCodeFirstUserMap(CodeFirstPojoIntf codeFirst) { User user1 = new User(); user1.setNames(new String[] {"u1", "u2"}); User user2 = new User(); user2.setNames(new String[] {"u3", "u4"}); Map userMap = new HashMap<>(); userMap.put("u1", user1); userMap.put("u2", user2); Map result = codeFirst.testUserMap(userMap); TestMgr.check("u1", result.get("u1").getNames()[0]); TestMgr.check("u2", result.get("u1").getNames()[1]); TestMgr.check("u3", result.get("u2").getNames()[0]); TestMgr.check("u4", result.get("u2").getNames()[1]); userMap = new HashMap<>(); userMap.put("u1", user1); userMap.put("u2", null); result = codeFirst.testUserMap(userMap); TestMgr.check(result.get("u1").getNames()[0], "u1"); TestMgr.check(result.get("u1").getNames()[1], "u2"); TestMgr.check(result.get("u2"), null); } private void testCodeFirstUserArray(CodeFirstPojoIntf codeFirst) { User user1 = new User(); user1.setNames(new String[] {"u1", "u2"}); User user2 = new User(); user2.setNames(new String[] {"u3", "u4"}); User[] users = new User[] {user1, user2}; List result = codeFirst.testUserArray(Arrays.asList(users)); TestMgr.check("u1", result.get(0).getNames()[0]); TestMgr.check("u2", result.get(0).getNames()[1]); TestMgr.check("u3", result.get(1).getNames()[0]); TestMgr.check("u4", result.get(1).getNames()[1]); } private void testCodeFirstStrings(CodeFirstPojoIntf codeFirst) { String[] result = codeFirst.testStrings(new String[] {"a", "b"}); TestMgr.check("aa0", result[0]); TestMgr.check("b", result[1]); result = codeFirst.testStrings(new String[] {"a", ""}); TestMgr.check("aa0", result[0]); TestMgr.check("", result[1]); } private void testCodeFirstBytes(CodeFirstPojoIntf codeFirst) { byte[] input = new byte[] {0, 1, 2}; byte[] result = codeFirst.testBytes(input); TestMgr.check(3, result.length); TestMgr.check(1, result[0]); TestMgr.check(1, result[1]); TestMgr.check(2, result[2]); } private void testCodeFirstAddDate(CodeFirstPojoIntf codeFirst) { Date date = new Date(); int seconds = 1; Date result = codeFirst.addDate(date, seconds); TestMgr.check(new Date(date.getTime() + seconds * 1000), result); } private void testCodeFirstAddString(CodeFirstPojoIntf codeFirst) { String result = codeFirst.addString(Arrays.asList("a", "b")); TestMgr.check("ab", result); } private void testCodeFirstIsTrue(CodeFirstPojoIntf codeFirst) { boolean result = codeFirst.isTrue(); TestMgr.check(true, result); } private void testCodeFirstSayHi2(CodeFirstPojoIntf codeFirst) { if (!CodeFirstPojoClientIntf.class.isInstance(codeFirst)) { return; } String result = ((CodeFirstPojoClientIntf) codeFirst).sayHi2("world"); TestMgr.check("world sayhi 2", result); } private void testCodeFirstSayHi(CodeFirstPojoIntf codeFirst) { String result = codeFirst.sayHi("world"); TestMgr.check("world sayhi, context k: null", result); // TestMgr.check(202, responseEntity.getStatusCode()); } private void testCodeFirstSaySomething(CodeFirstPojoIntf codeFirst) { Person person = new Person(); person.setName("person name"); String result = codeFirst.saySomething("prefix prefix", person); TestMgr.check("prefix prefix person name", result); } private void testCodeFirstSayHello(CodeFirstPojoIntf codeFirst) { Person input = new Person(); input.setName("person name"); Person result = codeFirst.sayHello(input); TestMgr.check("hello person name", result.getName()); input.setName(""); result = codeFirst.sayHello(input); TestMgr.check("hello ", result.getName()); } private void testCodeFirstReduce(CodeFirstPojoIntf codeFirst) { int result = codeFirst.reduce(5, 3); TestMgr.check(2, result); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClientIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.demo.CodeFirstPojoIntf; import io.swagger.v3.oas.annotations.Operation; public interface CodeFirstPojoClientIntf extends CodeFirstPojoIntf { @Operation(operationId = "sayHi", summary = "") CompletableFuture sayHiAsync(String name); String sayHi2(String name); } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ForkJoinPool; import java.util.stream.IntStream; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.Test; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.demo.smartcare.Application; import org.apache.servicecomb.demo.smartcare.Group; import org.apache.servicecomb.demo.smartcare.SmartCare; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class PojoClient { private static final Logger LOGGER = LoggerFactory.getLogger(PojoClient.class); // reference a not exist a microservice, and never use it // this should not cause problems @RpcReference(microserviceName = "notExist") public static Test notExist; @RpcReference(microserviceName = "pojo", schemaId = "server") public static Test test; public static Test testFromXml; private static SmartCare smartcare; public static void setTestFromXml(Test testFromXml) { PojoClient.testFromXml = testFromXml; } public static void main(String[] args) throws Exception { new SpringApplicationBuilder(PojoClient.class).web(WebApplicationType.NONE).run(args); try { run(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); LOGGER.info("-------------- last time updated checks(maybe more/less): 785 -------------"); } private static void testContextClassLoaderIsNull() throws Exception { ForkJoinPool pool = new ForkJoinPool(4); pool.submit(() -> IntStream.range(0, 20).parallel().forEach(item -> { if (Thread.currentThread().getName().equals("main")) { return; } // in web environment, this could be null, here we just mock a null class loader. Thread.currentThread().setContextClassLoader(null); TestMgr.check(null, test.postTestStatic(2)); })).get(); } public static void run() throws Exception { testHttpClientsIsOk(); CategorizedTestCaseRunner.runCategorizedTestCase("pojo"); smartcare = BeanUtils.getBean("smartcare"); String microserviceName = "pojo"; for (String transport : DemoConst.transports) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); LOGGER.info("test {}, transport {}", microserviceName, transport); testContextClassLoaderIsNull(); testNull(testFromXml); boolean checkerDestroyed = true; // Timer cancel may not destroy thread very fast so check for 3 times. for (int i = 0; i < 3; i++) { checkerDestroyed = true; Set allThreads = Thread.getAllStackTraces().keySet(); for (Thread t : allThreads) { if (t.getName().equals("NFLoadBalancer-serverWeightTimer-unknown")) { checkerDestroyed = false; Thread.sleep(1000); } } } TestMgr.check(checkerDestroyed, true); testSmartCare(smartcare); testCommonInvoke(transport); if ("rest".equals(transport)) { testTraceIdOnNotSetBefore(); } testTraceIdOnContextContainsTraceId(); } } private static void testHttpClientsIsOk() { TestMgr.check(HttpClients.getClient("config-center") != null, false); TestMgr.check(HttpClients.getClient("http-transport-client") != null, false); TestMgr.check(HttpClients.getClient("http2-transport-client") != null, true); TestMgr.check(HttpClients.getClient("config-center", false) != null, false); TestMgr.check(HttpClients.getClient("http-transport-client", false) != null, false); TestMgr.check(HttpClients.getClient("http2-transport-client", false) != null, true); } /** * Only in http transport, traceId will be set to invocation if null. * But in highway, nothing done. */ private static void testTraceIdOnNotSetBefore() { String traceId = test.testTraceId(); TestMgr.checkNotEmpty(traceId); } private static void testTraceIdOnContextContainsTraceId() { InvocationContext context = new InvocationContext(); context.addContext(CoreConst.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE)); ContextUtils.setInvocationContext(context); String traceId = test.testTraceId(); TestMgr.check(String.valueOf(Long.MIN_VALUE), traceId); ContextUtils.removeInvocationContext(); } private static void testSmartCare(SmartCare smartCare) { Group group = new Group(); group.setName("group0"); Application application = new Application(); application.setName("app0"); application.setDefaultGroup("group0"); application.setVersion("v1"); application.setDynamicFlag(true); List groups = new ArrayList<>(); groups.add(group); application.setGroups(groups); TestMgr.check("resultCode: 0\nresultMessage: add application app0 success", smartCare.addApplication(application)); TestMgr.check("resultCode: 1\nresultMessage: delete application app0 failed", smartCare.delApplication("app0")); } @SuppressWarnings("rawtypes") private static void testCommonInvoke(String transport) { Map arguments = new HashMap<>(); arguments.put("index", 2); arguments.put("user", new User()); Map warpArguments = new HashMap<>(); warpArguments.put("splitParamBody", arguments); User result = InvokerUtils.syncInvoke("pojo", "server", "splitParam", warpArguments, User.class); TestMgr.check("User [name=nameA, users count:0, age=100, index=2]", result); arguments = new HashMap<>(); arguments.put("index", 3); arguments.put("user", new User()); warpArguments = new HashMap<>(); warpArguments.put("splitParamBody", arguments); result = InvokerUtils.syncInvoke("pojo", transport, "server", "splitParam", warpArguments, User.class); TestMgr.check("User [name=nameA, users count:0, age=100, index=3]", result); } private static void testNull(Test test) { TestMgr.check("code is 'null'", test.getTestString(null)); TestMgr.check(null, test.postTestStatic(2)); TestMgr.check(null, test.patchTestStatic(2)); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemaInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; public interface SchemaInterface { String echo(String content); String echoError(String content); } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemeInterfacePojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; public interface SchemeInterfacePojo { int reduce(int a, int b); int add(int a, int b); } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestFlowControl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; @Component public class TestFlowControl implements CategorizedTestCase { interface Client { int foo(int num); int bar(int num); } @RpcReference(microserviceName = "pojo", schemaId = "FlowControlSchema") Client client1; @RpcReference(microserviceName = "pojo", schemaId = "FlowControlClientSchema") Client client2; @Override public void testAllTransport() throws Exception { testFlowControl((num) -> client1.foo(num), "provider", true); testFlowControl((num) -> client1.bar(num), "provider", false); testFlowControl((num) -> client2.foo(num), "consumer", true); testFlowControl((num) -> client2.bar(num), "consumer", false); } private void testFlowControl(Function function, String role, boolean expected) throws InterruptedException { AtomicBoolean failed = new AtomicBoolean(false); CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { new Thread(() -> { for (int i1 = 0; i1 < 10; i1++) { try { int result = function.apply(10); if (result != 10) { TestMgr.failed("", new Exception("not expected")); } } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 429); TestMgr.check(((CommonExceptionData) e.getErrorData()).getMessage(), role + " request rejected by flow control."); failed.set(true); break; } catch (Throwable e) { e.printStackTrace(); TestMgr.fail("not expected error " + e.getMessage()); } } countDownLatch.countDown(); }).start(); } countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check(expected, failed.get()); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestNotRecommendedService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.AbstractModel; import org.apache.servicecomb.demo.server.DefaultAbstractModel; import org.apache.servicecomb.demo.server.NotRecommendedServiceInf; import org.apache.servicecomb.demo.server.SecondAbstractModel; import org.apache.servicecomb.demo.server.WrappedAbstractModel; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestNotRecommendedService implements CategorizedTestCase { @RpcReference(microserviceName = "pojo", schemaId = "NotRecommendedService") private NotRecommendedServiceInf pojo; @Override public void testRestTransport() throws Exception { testLongMap(); testAbstractModel(); testListAbstractModel(); testMapAbstractModel(); testWrappedModel(); } private void testWrappedModel() { Map data = new HashMap<>(); AbstractModel model = new DefaultAbstractModel(); model.setName("hello"); data.put(100L, model); List data2 = new ArrayList<>(); AbstractModel model2 = new DefaultAbstractModel(); model2.setName("hello"); data2.add(model2); WrappedAbstractModel input = new WrappedAbstractModel(); input.setMapModel(data); input.setListModel(data2); AbstractModel secondModel = new SecondAbstractModel(); secondModel.setName("second"); input.setModel(secondModel); input.setName("wrapped"); WrappedAbstractModel result = pojo.wrappedAbstractModel(input); TestMgr.check(1, result.getMapModel().size()); TestMgr.check("hello", result.getMapModel().get(result.getMapModel().keySet().iterator().next()).getName()); TestMgr.check(1, result.getListModel().size()); TestMgr.check("hello", result.getListModel().get(0).getName()); TestMgr.check("second", result.getModel().getName()); TestMgr.check(true, result.getModel() instanceof SecondAbstractModel); TestMgr.check("wrapped", result.getName()); } private void testMapAbstractModel() { Map data = new HashMap<>(); AbstractModel model = new DefaultAbstractModel(); model.setName("hello"); data.put(100L, model); Map result = pojo.mapAbstractModel(data); TestMgr.check(1, result.size()); TestMgr.check("hello", result.get(result.keySet().iterator().next()).getName()); } private void testListAbstractModel() { List data = new ArrayList<>(); AbstractModel model = new DefaultAbstractModel(); model.setName("hello"); data.add(model); List result = pojo.listAbstractModel(data); TestMgr.check(1, result.size()); TestMgr.check("hello", result.get(0).getName()); } private void testAbstractModel() { AbstractModel model = new DefaultAbstractModel(); model.setName("hello"); AbstractModel result = pojo.abstractModel(model); TestMgr.check("hello", result.getName()); } private void testLongMap() { Map data = new HashMap<>(); data.put(100L, 200L); Map result = pojo.longMap(data); TestMgr.check(1, result.size()); TestMgr.check(200L, result.get(100L)); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestRpcReferenceMethodInjection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestRpcReferenceMethodInjection implements CategorizedTestCase { private SchemeInterfacePojo pojo; private SchemeInterfacePojo pojoValue; @RpcReference(microserviceName = "pojo", schemaId = "SchemeInterfacePojoImpl") public void setSchemeInterfacePojo(SchemeInterfacePojo pojo) { this.pojo = pojo; } @RpcReference(microserviceName = "${servicecomb.provider.name}", schemaId = "SchemeInterfacePojoImpl") public void setSchemeInterfacePojoValue(SchemeInterfacePojo pojoValue) { this.pojoValue = pojoValue; } @Override public void testAllTransport() throws Exception { TestMgr.check(-1, pojo.reduce(1, 2)); TestMgr.check(-1, pojoValue.reduce(1, 2)); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSameService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.servers.Server; @Component public class TestSameService implements CategorizedTestCase { @OpenAPIDefinition(servers = {@Server(url = "/SameInterface1")}) public interface SameInterface1 { String sayHello(String name); } @OpenAPIDefinition(servers = {@Server(url = "/SameInterface2")}) public interface SameInterface2 { String sayHello(String name); } @OpenAPIDefinition(servers = {@Server(url = "/SameService1")}) public interface SameService1 { String sayHello(String name); } @OpenAPIDefinition(servers = {@Server(url = "/SameService2")}) public interface SameService2 { String sayHello(String name); } @RpcReference(microserviceName = "pojo", schemaId = "SameService1") SameService1 sameService1; @RpcReference(microserviceName = "pojo", schemaId = "SameService2") SameService2 sameService2; @RpcReference(microserviceName = "pojo", schemaId = "SameInterface1") SameInterface1 sameInterface1; @RpcReference(microserviceName = "pojo", schemaId = "SameInterface2") SameInterface2 sameInterface2; @Override public void testAllTransport() throws Exception { TestMgr.check("pk1-svc-1", sameService1.sayHello("1")); TestMgr.check("pk2-svc-1", sameService2.sayHello("1")); TestMgr.check("pk1-inf-1", sameInterface1.sayHello("1")); TestMgr.check("pk2-inf-1", sameInterface2.sayHello("1")); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; @Component public class TestSchemeInterface implements CategorizedTestCase { @RpcReference(microserviceName = "pojo", schemaId = "SchemaInterface") private SchemaInterface pojo; @Override public void testAllTransport() throws Exception { TestMgr.check("hello", pojo.echo("hello")); try { pojo.echoError("hello"); TestMgr.failed("should throw exception", new Exception()); } catch (InvocationException e) { TestMgr.check( "Consumer method org.apache.servicecomb.demo.pojo.client.SchemaInterface:" + "echoError not exist in contract, microserviceName=pojo, schemaId=SchemaInterface.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterfacePojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; @Component public class TestSchemeInterfacePojo implements CategorizedTestCase { @RpcReference(microserviceName = "pojo", schemaId = "SchemeInterfacePojoImpl") private SchemeInterfacePojo pojo; @Override public void testAllTransport() throws Exception { TestMgr.check(-1, pojo.reduce(1, 2)); try { pojo.add(1, 2); TestMgr.failed("should throw exception", new Exception()); } catch (InvocationException e) { TestMgr.check( "Consumer method org.apache.servicecomb.demo.pojo.client.SchemeInterfacePojo:" + "add not exist in contract, microserviceName=pojo, " + "schemaId=SchemeInterfacePojoImpl.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.Arrays; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.Test; import org.apache.servicecomb.demo.server.TestRequest; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class TestTestImpl implements CategorizedTestCase { private static final Logger LOGGER = LoggerFactory.getLogger(TestTestImpl.class); private static final byte[] buffer = new byte[1024]; static { Arrays.fill(buffer, (byte) 1); } @RpcReference(microserviceName = "pojo", schemaId = "server") private Test test; @Override public void testAllTransport() throws Exception { testNull(test); // This test case shows destroy of WeightedResponseTimeRule timer task. after test finished will not print: // "Weight adjusting job started" and thread "NFLoadBalancer-serverWeightTimer-unknown" destroyed. InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "WeightedResponse"); testStringArray(test); InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "RoundRobin"); testStringArray(test); testChinese(test); testStringHaveSpace(test); testWrapParam(test); testSplitParam(test); testInputArray(test); testException(test); testIntArray(test); } @Override public void testRestTransport() throws Exception { testNullRest(test); testExceptionRest(test); testEmptyRest(test); } @Override public void testHighwayTransport() throws Exception { testNullHighway(test); testEmptyHighway(test); } private static void testIntArray(Test test) { int[] request = new int[] {5, 11, 4}; int[] result = test.testIntArray(request); TestMgr.check(request.length, result.length); TestMgr.check(request[1], result[1]); } private static void testEmptyHighway(Test test) { TestMgr.check("code is ''", test.getTestString("")); } private static void testEmptyRest(Test test) { TestMgr.check("code is ''", test.getTestString("")); } private static void testNullRest(Test test) { TestMgr.check(null, test.wrapParam(null)); } private static void testNullHighway(Test test) { TestMgr.check("nameA", test.wrapParam(null).getName()); } private static void testNull(Test test) { TestMgr.check("code is 'null'", test.getTestString(null)); TestMgr.check(null, test.postTestStatic(2)); TestMgr.check(null, test.patchTestStatic(2)); } private static void testChinese(Test test) { TestMgr.check("code is '测试'", test.getTestString("测试")); User user = new User(); user.setName("名字"); User result = test.splitParam(1, user); TestMgr.check("名字, users count:0", result.getName()); } private static void testStringHaveSpace(Test test) { TestMgr.check("code is 'a b'", test.getTestString("a b")); } private static void testStringArray(Test test) { // TestMgr.check("arr is '[a, , b]'", test.testStringArray(new String[] {"a", null, "b"})); TestMgr.check("arr is '[a, b]'", test.testStringArray(new String[] {"a", "b"})); } private static void testWrapParam(Test test) { User user = new User(); TestRequest request = new TestRequest(); request.setUser(user); request.setIndex(0); request.setData(buffer); request.getUsers().add(user); User result = test.wrapParam(request); LOGGER.info("wrap param result:{}", result); TestMgr.check("User [name=nameA, users count:1, age=100, index=0]", result); } private static void testExceptionRest(Test test) { try { test.testException(456); } catch (InvocationException e) { TestMgr.check("456 error", e.getErrorData()); } try { test.testException(556); } catch (InvocationException e) { TestMgr.check("[556 error]", e.getErrorData()); } try { test.testException(557); } catch (InvocationException e) { TestMgr.check("[[557 error]]", e.getErrorData()); } } private static void testException(Test test) { try { test.testException(456); } catch (InvocationException e) { TestMgr.check("456 error", e.getErrorData()); } try { test.testException(556); } catch (InvocationException e) { TestMgr.check("[556 error]", e.getErrorData()); } try { test.testException(557); } catch (InvocationException e) { TestMgr.check("[[557 error]]", e.getErrorData()); } } private static void testInputArray(Test test) { String result = test.addString(new String[] {"a", "b"}); LOGGER.info("input array result:{}", result); TestMgr.check("[a, b]", result); } private static void testSplitParam(Test test) { User result = test.splitParam(1, new User()); LOGGER.info("split param result:{}", result); TestMgr.check("User [name=nameA, users count:0, age=100, index=1]", result); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestWeakPojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.GenericsModel; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; /* This is the provider definition: @GetMapping(path = "/diffNames") @ApiOperation(value = "differentName", nickname = "differentName") public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) and the swagger is: paths: /diffNames: get: summary: "differentName" operationId: "differentName" parameters: - name: "x" in: "query" required: true type: "integer" format: "int32" - name: "y" in: "query" required: true type: "integer" format: "int32" responses: "200": description: "response of 200" schema: type: "integer" format: "int32" In consumer, you can define any prototype that matches generated swagger of provider. */ interface DiffNames { int differentName(int x, int y); } interface DiffNames2 { int differentName(int y, int x); } interface Generics { List> genericParams(int code, List> names); } interface GenericsModelInf { GenericsModel genericParamsModel(int code, GenericsModel model); } interface ObjectInf { GenericsModel obj(GenericsModel obj); } @Component public class TestWeakPojo implements CategorizedTestCase { @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") private DiffNames diffNames; @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") private DiffNames2 diffNames2; @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") private Generics generics; @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") private GenericsModelInf genericsModelInf; @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") private ObjectInf objectInf; @Override public void testRestTransport() throws Exception { } @Override public void testHighwayTransport() throws Exception { } @Override public void testAllTransport() throws Exception { testDiffName(); testGenerics(); testGenericsModel(); testObject(); } private void testObject() throws JsonProcessingException { GenericsModel model = new GenericsModel(); model.setName("model"); List> namesList = new ArrayList<>(); List names = new ArrayList<>(); names.add("hello"); namesList.add(names); model.setNameList(namesList); List>> objectLists = new ArrayList<>(); List> objectList = new ArrayList<>(); List objects = new ArrayList<>(); objects.add("object"); objectList.add(objects); objectLists.add(objectList); model.setObjectLists(objectLists); GenericsModel result = objectInf.obj(model); TestMgr.check(JsonUtils.writeValueAsString(model), JsonUtils.writeValueAsString(result)); } private void testGenericsModel() throws JsonProcessingException { GenericsModel model = new GenericsModel(); model.setName("model"); List> namesList = new ArrayList<>(); List names = new ArrayList<>(); names.add("hello"); namesList.add(names); model.setNameList(namesList); List>> objectLists = new ArrayList<>(); List> objectList = new ArrayList<>(); List objects = new ArrayList<>(); objects.add("object"); objectList.add(objects); objectLists.add(objectList); model.setObjectLists(objectLists); GenericsModel result = genericsModelInf.genericParamsModel(100, model); TestMgr.check(JsonUtils.writeValueAsString(model), JsonUtils.writeValueAsString(result)); } private void testGenerics() { List> namesList = new ArrayList<>(); List names = new ArrayList<>(); names.add("hello"); namesList.add(names); List> nameListResult = generics.genericParams(100, namesList); TestMgr.check(1, nameListResult.size()); TestMgr.check(1, nameListResult.get(0).size()); TestMgr.check("hello", nameListResult.get(0).get(0)); } private void testDiffName() { TestMgr.check(7, diffNames.differentName(2, 3)); TestMgr.check(8, diffNames2.differentName(2, 3)); Map args = new HashMap<>(); args.put("x", 2); args.put("y", 3); Map swaggerArgs = new HashMap<>(); swaggerArgs.put("differentNameBody", args); TestMgr.check(7, InvokerUtils.syncInvoke("pojo", "WeakPojo", "differentName", swaggerArgs, Integer.class)); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/ClientModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client.invoker; public class ClientModel { private String name; private int code; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/TestInvokerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.client.invoker; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.stereotype.Component; @Component @SuppressWarnings({"unchecked", "rawtypes"}) public class TestInvokerEndpoint implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { testInvokerUtilsDiffModelRest(); } @Override public void testHighwayTransport() throws Exception { testInvokerUtilsDiffModelHighway(); } @Override public void testAllTransport() throws Exception { testInvokerUtilsDiffModel(); testInvokerUtilsDiffModelMapArgs(); } private void testInvokerUtilsDiffModelHighway() throws Exception { Map args = new HashMap<>(); ClientModel model = new ClientModel(); model.setCode(200); model.setName("hello"); args.put("request", model); Map result = InvokerUtils.syncInvoke("pojo", "highway", "InvokerEndpoint", "model", args, Map.class); TestMgr.check(model.getCode(), result.get("code")); TestMgr.check(model.getName(), result.get("name")); ClientModel modelResult = InvokerUtils .syncInvoke("pojo", "highway", "InvokerEndpoint", "model", args, ClientModel.class); TestMgr.check(model.getCode(), modelResult.getCode()); TestMgr.check(model.getName(), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); InvokerUtils .reactiveInvoke("pojo", "highway", "InvokerEndpoint", "model", args, ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.getCode(), reactiveResult.getCode()); TestMgr.check(model.getName(), reactiveResult.getName()); System.out.println("done"); countDownLatch.countDown(); }); countDownLatch.await(); } private void testInvokerUtilsDiffModelRest() throws Exception { Map args = new HashMap<>(); ClientModel model = new ClientModel(); model.setCode(200); model.setName("hello"); args.put("request", model); Map result = InvokerUtils.syncInvoke("pojo", "rest", "InvokerEndpoint", "model", args, Map.class); TestMgr.check(model.getCode(), result.get("code")); TestMgr.check(model.getName(), result.get("name")); ClientModel modelResult = InvokerUtils .syncInvoke("pojo", "rest", "InvokerEndpoint", "model", args, ClientModel.class); TestMgr.check(model.getCode(), modelResult.getCode()); TestMgr.check(model.getName(), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); InvokerUtils.reactiveInvoke("pojo", "rest", "InvokerEndpoint", "model", args, ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.getCode(), reactiveResult.getCode()); TestMgr.check(model.getName(), reactiveResult.getName()); System.out.println("done"); countDownLatch.countDown(); }); countDownLatch.await(); } private void testInvokerUtilsDiffModel() throws Exception { Map args = new HashMap<>(); ClientModel model = new ClientModel(); model.setCode(200); model.setName("hello"); args.put("request", model); Map result = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args, Map.class); TestMgr.check(model.getCode(), result.get("code")); TestMgr.check(model.getName(), result.get("name")); ClientModel modelResult = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class); TestMgr.check(model.getCode(), modelResult.getCode()); TestMgr.check(model.getName(), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); InvokerUtils.reactiveInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.getCode(), reactiveResult.getCode()); TestMgr.check(model.getName(), reactiveResult.getName()); System.out.println("done"); countDownLatch.countDown(); }); countDownLatch.await(); } private void testInvokerUtilsDiffModelMapArgs() throws Exception { Map args = new HashMap<>(); Map model = new HashMap(); model.put("code", 20); model.put("name", "hello"); args.put("request", model); Map result = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args, Map.class); TestMgr.check(model.get("code"), result.get("code")); TestMgr.check(model.get("name"), result.get("name")); ClientModel modelResult = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class); TestMgr.check(model.get("code"), modelResult.getCode()); TestMgr.check(model.get("name"), modelResult.getName()); CountDownLatch countDownLatch = new CountDownLatch(1); InvokerUtils.reactiveInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class, response -> { ClientModel reactiveResult = response.getResult(); TestMgr.check(model.get("code"), reactiveResult.getCode()); TestMgr.check(model.get("name"), reactiveResult.getName()); countDownLatch.countDown(); }); countDownLatch.await(); } } ================================================ FILE: demo/demo-pojo/pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml ================================================ ================================================ FILE: demo/demo-pojo/pojo-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: pojotest name: pojoClient version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest.client.enabled: false # using only http2 swagger: disableDataTypeCheck: true isolation: Consumer: enabled: false loadbalance: strategy: name: Random metrics: window_time: 12000 publisher.defaultLog.enabled: false # when in testing , can turn on flowcontrol: enabled: true Consumer: qps: limit: pojo: FlowControlClientSchema: foo: 3 bar: 3000 provider: name: pojo ================================================ FILE: demo/demo-pojo/pojo-client/src/test/java/org/apache/servicecomb/demo/pojo/PojoIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.pojo.client.PojoClient; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = PojoClient.class) public class PojoIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { PojoClient.run(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-pojo/pojo-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-pojo 3.4.0-SNAPSHOT pojo-server Java Chassis::Demo::POJO::Server org.apache.servicecomb.demo demo-schema org.apache.servicecomb.demo.pojo.server.PojoServer true org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/CodeFirstPojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.demo.CodeFirstPojoIntf; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.mapnull.ParseRequest; import org.apache.servicecomb.demo.mapnull.ParseResponse; import org.apache.servicecomb.demo.server.MapModel; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.servers.Server; @RpcSchema() @OpenAPIDefinition(servers = {@Server(url = "/pojo/rest")}) public class CodeFirstPojo implements CodeFirstPojoIntf { @Override public ParseResponse parse(ParseRequest request) { ParseResponse r = new ParseResponse(); r.setResultCode("CU1I0000"); r.setResultInfo("报文处理成功!报文解析成功!"); r.setMsgType("cncc.111.001.01"); Map h = new HashMap<>(); h.put("C11", "20200101"); h.put("F41", "3"); h.put("F40", "cncc.111.001.01"); h.put("E24", "HVPA5C30177808463743"); h.put("E35", "HVPA5C30177808463743"); h.put("A29", "{N:, K20=}"); h.put("F38", "HVPS"); h.put("K13", "01"); h.put("F39", "SAPS"); h.put("K14", "CMT"); h.put("K16", ""); h.put("C92", "102633"); r.setMsgHeader(h); Map b = new HashMap<>(); b.put("E50", "hvps.141.001.01"); b.put("A00", "402451000010"); b.put("A01", "105100000017"); b.put("F32", "NORM"); b.put("E57", "19218385"); b.put("D14", "200000.00"); b.put("C14", "20200101"); b.put("F25", "02711"); b.put("A70", "908100000002"); b.put("C92", "20200101"); b.put("F2H", "G105"); r.setMsgBody(b); return r; } @Override public MapModel testMapModel(MapModel model) { return model; } @Override public Map testMap(Map map) { return map; } @Override public Map testUserMap(Map userMap) { return userMap; } @Override public List testUserArray(List users) { return users; } public String[] testStrings(String[] input) { input[0] += input[0] + "0"; return input; } public byte[] testBytes(byte[] input) { input[0] = (byte) (input[0] + 1); return input; } public int reduce(int a, int b) { return a - b; } public Date addDate(Date date, long second) { return new Date(date.getTime() + second * 1000); } public Person sayHello(Person user) { user.setName("hello " + user.getName()); return user; } public String saySomething(String prefix, Person user) { return prefix + " " + user.getName(); } public String sayHi(String name) { ContextUtils.getInvocationContext().setStatus(202); return name + " sayhi, context k: " + (ContextUtils.getInvocationContext() == null ? "" : ContextUtils.getInvocationContext().getContext("k")); } @Operation(operationId = "sayHi2", summary = "") public CompletableFuture sayHi2Async(String name) { CompletableFuture future = new CompletableFuture<>(); future.complete(name + " sayhi 2"); return future; } public boolean isTrue() { return true; } public String addString(List s) { StringBuilder result = new StringBuilder(); for (String x : s) { result.append(x); } return result.toString(); } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlClientSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "FlowControlClientSchema") public class FlowControlClientSchema { public int foo(int num) { return num; } public int bar(int num) { return num; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "FlowControlSchema") public class FlowControlSchema { public int foo(int num) { return num; } public int bar(int num) { return num; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/HelloImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.apache.servicecomb.demo.helloworld.greeter.Hello; public class HelloImpl implements Hello { @Override public String SayHello(String name) { return "Hello Message fast"; } @Override public String SayHelloAgain(String name) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello Message slow"; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/NotRecommendedService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import java.util.List; import java.util.Map; import org.apache.servicecomb.demo.server.AbstractModel; import org.apache.servicecomb.demo.server.NotRecommendedServiceInf; import org.apache.servicecomb.demo.server.WrappedAbstractModel; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "NotRecommendedService", schemaInterface = NotRecommendedServiceInf.class) public class NotRecommendedService implements NotRecommendedServiceInf { @Override public Map longMap(Map map) { return map; } @Override public List listAbstractModel(List listModel) { return listModel; } @Override public AbstractModel abstractModel(AbstractModel model) { return model; } @Override public Map mapAbstractModel(Map mapModel) { return mapModel; } @Override public WrappedAbstractModel wrappedAbstractModel(WrappedAbstractModel wrappedModel) { return wrappedModel; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoProducersCustomized.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.apache.servicecomb.provider.pojo.schema.PojoProducers; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @Primary @Component // This example shows user's how to customize PojoProducers by @Primary annotation public class PojoProducersCustomized extends PojoProducers { } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class PojoServer { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder(PojoServer.class).web(WebApplicationType.NONE).run(args); } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; public interface SchemaInterface { String echo(String content); } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterfaceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; public class SchemaInterfaceImpl implements SchemaInterface { @Override public String echo(String content) { return content; } public String echoError(String content) { return content; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; public interface SchemeInterfacePojo { int reduce(int a, int b); } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojoImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "SchemeInterfacePojoImpl", schemaInterface = SchemeInterfacePojo.class) public class SchemeInterfacePojoImpl implements SchemeInterfacePojo { @Override public int reduce(int a, int b) { return a - b; } public int add(int a, int b) { return a + b; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SmartCareImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import org.apache.servicecomb.demo.smartcare.Application; import org.apache.servicecomb.demo.smartcare.Response; import org.apache.servicecomb.demo.smartcare.SmartCare; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SmartCareImpl implements SmartCare { private static final Logger LOG = LoggerFactory.getLogger(SmartCareImpl.class); @Override public Response addApplication(Application application) { LOG.info(application.toString()); Response resp = new Response(); resp.setResultCode(0); resp.setResultMessage("add application " + application.getName() + " success"); return resp; } @SuppressWarnings("divzero") @Override public Response delApplication(String appName) { LOG.info(appName); try { System.out.println(5 / 0); } catch (Exception e) { Response resp = new Response(); resp.setResultCode(1); resp.setResultMessage("delete application " + appName + " failed"); return resp; } return null; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.server.Test; import org.apache.servicecomb.demo.server.TestRequest; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @RpcSchema(schemaId = "server") public class TestImpl implements Test { @Override public String testStringArray(String[] arr) { return String.format("arr is '%s'", Arrays.toString(arr)); } @Override public String getTestString(String code) { return String.format("code is '%s'", code); } @Override public String postTestStatic(int code) { return null; } @Override public String patchTestStatic(int code) { return null; } private User doTest(int index, User user, List users, byte[] data) { if (user == null) { user = new User(); } user.setIndex(index); int userCount = (users == null) ? 0 : users.size(); user.setName(user.getName() + ", users count:" + userCount); return user; } @Override public String testException(int code) { String strCode = String.valueOf(code); switch (code) { case 200: return strCode; case 456: throw new InvocationException(code, strCode, strCode + " error"); case 556: throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); case 557: throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); default: break; } return "not expected"; } @Override public User splitParam(int index, User user) { return doTest(index, user, null, null); } @Override public User wrapParam(TestRequest request) { if (request == null || request.getUser() == null) { return null; } return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); } @Override public String addString(String[] strArr) { String result = Arrays.toString(strArr); System.out.println("addString: " + result); return result; } @Override public String testTraceId() { return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); } @Override public int[] testIntArray(int[] request) { return request; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/WeakPojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server; import java.util.List; import org.apache.servicecomb.demo.server.GenericsModel; import org.apache.servicecomb.provider.pojo.RpcSchema; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @RpcSchema(schemaId = "WeakPojo") public class WeakPojo { @Operation(summary = "differentName", operationId = "differentName") public int diffNames(@Parameter(name = "x") int a, @Parameter(name = "y") int b) { return a * 2 + b; } public List> genericParams(int code, List> names) { return names; } public GenericsModel genericParamsModel(int code, GenericsModel model) { return model; } public Object obj(Object obj) { return obj; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/InvokerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.invoker; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "InvokerEndpoint") public class InvokerEndpoint { public ServerModel model(ServerModel request) { return request; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/ServerModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.invoker; public class ServerModel { private String name; private int code; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.same.pk1; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.servers.Server; @OpenAPIDefinition(servers = {@Server(url = "/SameInterface1")}) public interface SameInterface { String sayHello(String name); } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterfaceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.same.pk1; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.stereotype.Component; @RpcSchema(schemaId = "SameInterface1", schemaInterface = SameInterface.class) @Component("SameInterface1") public class SameInterfaceImpl implements SameInterface { @Override public String sayHello(String name) { return "pk1-inf-" + name; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.same.pk1; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.stereotype.Component; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.servers.Server; @RpcSchema(schemaId = "SameService1") @Component("SameService1") @OpenAPIDefinition(servers = {@Server(url = "/SameService1")}) public class SameService { public String sayHello(String name) { return "pk1-svc-" + name; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.same.pk2; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.servers.Server; @OpenAPIDefinition(servers = {@Server(url = "/SameInterface2")}) public interface SameInterface { String sayHello(String name); } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterfaceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.same.pk2; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.stereotype.Component; @RpcSchema(schemaId = "SameInterface2", schemaInterface = SameInterface.class) @Component("SameInterface2") public class SameInterfaceImpl implements SameInterface { @Override public String sayHello(String name) { return "pk2-inf-" + name; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.pojo.server.same.pk2; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.stereotype.Component; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.servers.Server; @RpcSchema(schemaId = "SameService2") @Component("SameService2") @OpenAPIDefinition(servers = {@Server(url = "/SameService2")}) public class SameService { public String sayHello(String name) { return "pk2-svc-" + name; } } ================================================ FILE: demo/demo-pojo/pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml ================================================ ================================================ FILE: demo/demo-pojo/pojo-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: pojotest name: pojo version: 0.0.4 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080?protocol=http2 server: http2: useAlpnEnabled: false highway: address: 0.0.0.0:7070 swagger: disableDataTypeCheck: true flowcontrol: enabled: true Provider: qps: limit: ANY: FlowControlSchema: foo: 3 bar: 3000 ================================================ FILE: demo/demo-pojo/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-pojo Java Chassis::Demo::POJO pom pojo-server pojo-client org.apache.servicecomb solution-basic org.apache.servicecomb registry-service-center org.apache.servicecomb handler-publickey-auth org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-register-url-prefix/README.md ================================================ This demo is an integration test for testing ```yaml servicecomb: service: registry: registerUrlPrefix: true ``` When this configuration is enabled, web context path is added to swagger, and consumer can invoke with context path ```yaml template.getForObject( "cse://demo-register-url-prefix-server/hellodemo/register/url/prefix/getName?name=2", String.class) ``` This feature is not recommended for use by default, but for some backward capabilities. ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml ================================================ 4.0.0 demo-register-url-prefix-client Java Chassis::Demo::Register URL Prefix client org.apache.servicecomb.demo demo-register-url-prefix 3.4.0-SNAPSHOT org.apache.servicecomb.demo.prefix.Application org.apache.servicecomb.demo demo-schema docker demo-register-url-prefix-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/java/org/apache/servicecomb/demo/prefix/Application.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.prefix; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.web.client.RestOperations; @SpringBootApplication public class Application { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.SERVLET).build().run(args); runTest(); } public static void runTest() { RestOperations template = RestTemplateBuilder.create(); TestMgr.check("2", template .getForObject("cse://demo-register-url-prefix-server/hellodemo/register/url/prefix/getName?name=2", String.class)); TestMgr.summary(); } } ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8082 servicecomb: service: application: demo-register-url-prefix name: demo-register-url-prefix-client version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 handler: chain: Consumer: default: loadbalance ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-client/src/test/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.prefix; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.SpringApplication; public class RegisterUrlPrefixIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { SpringApplication.run(Application.class); Application.runTest(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml ================================================ 4.0.0 demo-register-url-prefix-server Java Chassis::Demo::Register URL Prefix Server org.apache.servicecomb.demo demo-register-url-prefix 3.4.0-SNAPSHOT org.apache.servicecomb.demo.prefix.PrefixApplication jakarta.ws.rs jakarta.ws.rs-api org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/PrefixApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.prefix; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class PrefixApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(PrefixApplication.class).web(WebApplicationType.SERVLET).build().run(args); } } ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.prefix; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "RegisterUrlPrefixEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class RegisterUrlPrefixEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: servlet: context-path: /hellodemo port: 8080 servicecomb: service: application: demo-register-url-prefix name: demo-register-url-prefix-server version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 registerUrlPrefix: true rest: address: 0.0.0.0:8080 ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/logback.xml ================================================ %d [%level] [%thread] - %msg (%F:%L\)%n ================================================ FILE: demo/demo-register-url-prefix/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-register-url-prefix Java Chassis::Demo::Register URL Prefix pom demo-register-url-prefix-server demo-register-url-prefix-client org.apache.servicecomb solution-basic org.apache.servicecomb registry-service-center org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core org.apache.servicecomb foundation-test-scaffolding compile ================================================ FILE: demo/demo-schema/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-schema Java Chassis::Demo::Schema org.apache.servicecomb solution-basic org.apache.servicecomb foundation-test-scaffolding compile org.apache.maven.plugins maven-jar-plugin config/log4j.eclipse.properties ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; public interface CategorizedTestCase { /** * test case which only successful in REST transport */ default void testRestTransport() throws Exception { } /** * test case which only successful in HIGHWAY transport */ default void testHighwayTransport() throws Exception { } /** * test case which successful in both REST and HIGHWAY transport */ default void testAllTransport() throws Exception { } default String getMicroserviceName() { return null; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCaseRunner.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import java.util.Map; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CategorizedTestCaseRunner { private static final Logger LOGGER = LoggerFactory.getLogger(CategorizedTestCaseRunner.class); public static void runCategorizedTestCase(String microserviceName) throws Exception { Map tests = BeanUtils.getContext().getBeansOfType(CategorizedTestCase.class); for (String transport : DemoConst.transports) { for (CategorizedTestCase testCase : tests.values()) { try { if (testCase.getMicroserviceName() != null) { changeTransport(testCase.getMicroserviceName(), transport); } else { changeTransport(microserviceName, transport); } testCase.testAllTransport(); if ("rest".equals(transport)) { testCase.testRestTransport(); } else if ("highway".equals(transport)) { testCase.testHighwayTransport(); } } catch (Exception e) { LOGGER.error("run categorized test case " + testCase.getClass().getName() + " failed.", e); throw e; } } } } public static void changeTransport(String microserviceName, String transport) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstPojoIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.mapnull.ParseRequest; import org.apache.servicecomb.demo.mapnull.ParseResponse; import org.apache.servicecomb.demo.server.MapModel; import org.apache.servicecomb.demo.server.User; public interface CodeFirstPojoIntf { ParseResponse parse(ParseRequest request); MapModel testMapModel(MapModel model); Map testMap(Map map); Map testUserMap(Map userMap); List testUserArray(List users); String[] testStrings(String[] input); byte[] testBytes(byte[] input); int reduce(int a, int b); Date addDate(Date date, long second); Person sayHello(Person user); String saySomething(String prefix, Person user); String sayHi(String name); boolean isTrue(); String addString(List s); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore; import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestOperations; import io.vertx.core.json.JsonObject; public class CodeFirstRestTemplate { protected Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } protected void changeTransport(String microserviceName, String transport) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); } public void testCodeFirst(RestOperations template, String microserviceName, String basePath) { String cseUrlPrefix = "cse://" + microserviceName + basePath; changeTransport(microserviceName, "highway"); testOnlyHighway(template, cseUrlPrefix); changeTransport(microserviceName, CoreConst.RESTFUL); testOnlyRest(microserviceName, template, cseUrlPrefix); for (String transport : DemoConst.transports) { changeTransport(microserviceName, transport); testAllTransport(microserviceName, template, cseUrlPrefix); } } protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { testCodeFirstUserMap(template, cseUrlPrefix); testCodeFirstTextPlain(template, cseUrlPrefix); testCodeFirstBytes(template, cseUrlPrefix); testCseResponse(microserviceName, template, cseUrlPrefix); testCodeFirstAddDate(template, cseUrlPrefix); testCodeFirstAdd(template, cseUrlPrefix); testCodeFirstAddString(template, cseUrlPrefix); testCodeFirstIsTrue(template, cseUrlPrefix); testCodeFirstSayHi2(template, cseUrlPrefix); testCodeFirstSayHi(template, cseUrlPrefix); testCodeFirstSaySomething(template, cseUrlPrefix); testCodeFirstSayHello(template, cseUrlPrefix); testCodeFirstReduce(template, cseUrlPrefix); testTraceIdOnContextContainsTraceId(template, cseUrlPrefix); testRawJson(template, cseUrlPrefix); } protected void testOnlyHighway(RestOperations template, String cseUrlPrefix) { } protected void testOnlyRest(String microserviceName, RestOperations template, String cseUrlPrefix) { testCodeFirstUserMap(template, cseUrlPrefix); testCodeFirstTextPlain(template, cseUrlPrefix); testCodeFirstBytes(template, cseUrlPrefix); testCseResponse(microserviceName, template, cseUrlPrefix); testCodeFirstAddDate(template, cseUrlPrefix); testCodeFirstAdd(template, cseUrlPrefix); testCodeFirstAddString(template, cseUrlPrefix); testModelFieldIgnore(template, cseUrlPrefix); testCodeFirstReduce(template, cseUrlPrefix); // only rest transport will set trace id testTraceIdOnNotSetBefore(template, cseUrlPrefix); } private void testCodeFirstUserMap(RestOperations template, String cseUrlPrefix) { User user1 = new User(); user1.setNames(new String[] {"u1", "u2"}); User user2 = new User(); user2.setNames(new String[] {"u3", "u4"}); Map userMap = new HashMap<>(); userMap.put("u1", user1); userMap.put("u2", user2); // TODO: shall we support this usage? Seams not valid, cause result should be Map and type not defined. // @SuppressWarnings("unchecked") // Map result = template.postForObject(cseUrlPrefix + "testUserMap", // userMap, // Map.class); Map result = template.exchange(cseUrlPrefix + "testUserMap", HttpMethod.POST, new HttpEntity<>(userMap), new ParameterizedTypeReference>() { }).getBody(); TestMgr.check("u1", result.get("u1").getNames()[0]); TestMgr.check("u2", result.get("u1").getNames()[1]); TestMgr.check("u3", result.get("u2").getNames()[0]); TestMgr.check("u4", result.get("u2").getNames()[1]); } private void testCodeFirstTextPlain(RestOperations template, String cseUrlPrefix) { String body = "a=1"; String result = template.postForObject(cseUrlPrefix + "textPlain", body, String.class); TestMgr.check(body, result); } private void testCodeFirstBytes(RestOperations template, String cseUrlPrefix) { byte[] body = new byte[] {0, 1, 2}; byte[] result = template.postForObject(cseUrlPrefix + "bytes", body, byte[].class); TestMgr.check(3, result.length); TestMgr.check(1, result[0]); TestMgr.check(1, result[1]); TestMgr.check(2, result[2]); } protected void checkStatusCode(String microserviceName, int expectStatusCode, HttpStatusCode httpStatus) { TestMgr.check(expectStatusCode, httpStatus.value()); } private void testCseResponse(String targetMicroserviceName, RestOperations template, String cseUrlPrefix) { String srcMicroserviceName = BootStrapProperties.readServiceName(environment); ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "cseResponse", HttpMethod.GET, null, User.class); TestMgr.check("User [name=nameA, age=100, index=0]", responseEntity.getBody()); TestMgr.check("h1v " + srcMicroserviceName, responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v " + srcMicroserviceName, responseEntity.getHeaders().getFirst("h2")); checkStatusCode(targetMicroserviceName, 202, responseEntity.getStatusCode()); } private void testCodeFirstAddDate(RestOperations template, String cseUrlPrefix) { Map body = new HashMap<>(); Date date = new Date(); body.put("date", date); int seconds = 1; Date result = template.postForObject(cseUrlPrefix + "addDate?seconds={seconds}", body, Date.class, seconds); TestMgr.check(new Date(date.getTime() + seconds * 1000), result); } private void testCodeFirstAddString(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "addstring?s=a&s=b", HttpMethod.DELETE, null, String.class); TestMgr.check("ab", responseEntity.getBody()); } private void testCodeFirstIsTrue(RestOperations template, String cseUrlPrefix) { boolean result = template.getForObject(cseUrlPrefix + "istrue", boolean.class); TestMgr.check(true, result); } private void testCodeFirstSayHi2(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "sayhi/{name}/v2", HttpMethod.PUT, null, String.class, "world"); TestMgr.check("world sayhi 2", responseEntity.getBody()); } private void testCodeFirstSayHi(RestOperations template, String cseUrlPrefix) { ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "world"); TestMgr.check(202, responseEntity.getStatusCode().value()); TestMgr.check("world sayhi", responseEntity.getBody()); } private void testCodeFirstSaySomething(RestOperations template, String cseUrlPrefix) { Person person = new Person(); person.setName("person name"); HttpHeaders headers = new HttpHeaders(); headers.add("prefix", "prefix prefix"); headers.add("userId", "serviceCombUser"); HttpEntity requestEntity = new HttpEntity<>(person, headers); String result = template.postForObject(cseUrlPrefix + "saysomething", requestEntity, String.class); TestMgr.check("prefix prefix person name", result); } private void testCodeFirstSayHello(RestOperations template, String cseUrlPrefix) { Map persionFieldMap = new HashMap<>(); persionFieldMap.put("name", "person name from map"); Person result = template.postForObject(cseUrlPrefix + "sayhello", persionFieldMap, Person.class); TestMgr.check("hello person name from map", result); Person input = new Person(); input.setName("person name from Object"); result = template.postForObject(cseUrlPrefix + "sayhello", input, Person.class); TestMgr.check("hello person name from Object", result); } private void testCodeFirstAdd(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); params.put("b", "3"); int result = template.postForObject(cseUrlPrefix + "add", params, Integer.class); TestMgr.check(8, result); } private void testCodeFirstReduce(RestOperations template, String cseUrlPrefix) { Map params = new HashMap<>(); params.put("a", "5"); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.COOKIE, "b=3"); HttpEntity requestEntity = new HttpEntity<>(headers); ResponseEntity result = template.exchange(cseUrlPrefix + "reduce?a={a}", HttpMethod.GET, requestEntity, Integer.class, params); TestMgr.check(2, result.getBody()); } private void testModelFieldIgnore(RestOperations template, String cseUrlPrefix) { InputModelForTestIgnore input = new InputModelForTestIgnore("input_id_rest", "input_id_content", new Person("inputSomeone"), new JsonObject("{\"InputJsonKey\" : \"InputJsonValue\"}"), () -> { }); OutputModelForTestIgnore output = template .postForObject(cseUrlPrefix + "ignore", input, OutputModelForTestIgnore.class); TestMgr.check(null, output.getInputId()); TestMgr.check(input.getContent(), output.getContent()); TestMgr.check(null, output.getOutputId()); TestMgr.check(null, output.getInputIgnoreInterface()); TestMgr.check(null, output.getInputJsonObject()); TestMgr.check(null, output.getInputObject()); TestMgr.check(null, output.getOutputIgnoreInterface()); TestMgr.check(null, output.getOutputJsonObject()); TestMgr.check(null, output.getOutputObject()); } private void testRawJson(RestOperations template, String cseUrlPrefix) { String input = "{\"name\" : \"zyy\"}"; String output = template.postForObject(cseUrlPrefix + "rawJsonAnnotation", input, String.class); TestMgr.check("hello zyy", output); } private void testTraceIdOnNotSetBefore(RestOperations template, String cseUrlPrefix) { String traceIdUrl = cseUrlPrefix + "traceId"; String result = template.getForObject(traceIdUrl, String.class); TestMgr.checkNotEmpty(result); } private void testTraceIdOnContextContainsTraceId(RestOperations template, String cseUrlPrefix) { String traceIdUrl = cseUrlPrefix + "traceId"; InvocationContext invocationContext = new InvocationContext(); invocationContext.addContext(CoreConst.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE)); ContextUtils.setInvocationContext(invocationContext); String result = template.getForObject(traceIdUrl, String.class); TestMgr.check(String.valueOf(Long.MIN_VALUE), result); ContextUtils.removeInvocationContext(); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CommonSchemaInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.Operation; @RequestMapping("/CommonSchemaInterface") public interface CommonSchemaInterface { @GetMapping(path = "testInvocationTimeout") String testInvocationTimeout(@RequestParam("timeout") long timeout, @RequestParam("name") String name); @GetMapping(path = "testInvocationTimeoutWithInvocation") @Operation(summary = "testInvocationTimeoutWithInvocation", operationId = "testInvocationTimeoutWithInvocation") String testInvocationTimeout(InvocationContext context, @RequestParam("timeout") long timeout, @RequestParam("name") String name); @GetMapping(path = "testInvocationTimeoutInClientWait") String testInvocationTimeoutInClientWait(@RequestParam("timeout") long timeout, @RequestParam("name") String name); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import org.apache.servicecomb.core.CoreConst; public interface DemoConst { String[] transports = new String[] {"rest", "highway", CoreConst.ANY_TRANSPORT}; } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoSSLCustom.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import java.io.File; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DemoSSLCustom extends SSLCustom { private static final Logger LOGGER = LoggerFactory.getLogger(DemoSSLCustom.class); @Override public char[] decode(char[] encrypted) { return encrypted; } @Override public String getFullPath(String filename) { LOGGER.info("current working dir :" + System.getProperty("user.dir")); if (StringUtils.isEmpty(filename)) { return null; } // local for different IDEs File localFile = new File( System.getProperty("user.dir") + "/demo/demo-springmvc/springmvc-server/src/main/resources/certificates/" + filename); if (localFile.isFile()) { return localFile.getAbsolutePath(); } localFile = new File( System.getProperty("user.dir") + "/src/main/resources/certificates/" + filename); if (localFile.isFile()) { return localFile.getAbsolutePath(); } localFile = new File(System.getProperty("user.dir") + "/certificates/" + filename); if (localFile.isFile()) { return localFile.getAbsolutePath(); } // docker localFile = new File("/maven/maven/certificates/" + filename); if (localFile.isFile()) { return localFile.getAbsolutePath(); } // in jar, maybe LOGGER.info("not found file {} in file system, maybe in jar.", filename); return "certificates/" + filename; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/EmptyObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; public class EmptyObject { } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; public class Generic { public T value; } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import com.fasterxml.jackson.databind.JavaType; /** * Demonstrate how to using String as raw type when using RestTemplate to invoke a service that use POJO. e.g. *

* Provider:

* * public Response errorCodeWithHeader(MultiRequest request) * *

* Consumer:

* * String stringRequest = "{\"key\":\"testValue\"}"; * template.postForEntity(url, stringRequest, MultiResponse200.class); * *

* Caution: json will convert String to object based on String constructor, using this feature will make default * conversion change. You must write convertValue to check possible types using. */ public class RestObjectMapperWithStringMapper extends RestObjectMapper { private static final long serialVersionUID = 4279371572149490568L; private static Logger LOGGER = LoggerFactory.getLogger(RestObjectMapperWithStringMapper.class); public RestObjectMapperWithStringMapper() { super(); } @Override public T convertValue(Object fromValue, JavaType toValueType) throws IllegalArgumentException { if (String.class.isInstance(fromValue) && !BeanUtils.isSimpleValueType(toValueType.getRawClass())) { try { return super.readValue((String) fromValue, toValueType); } catch (IOException e) { LOGGER.error("Failed to convert value for {}.", e.getMessage()); } } return super.convertValue(fromValue, toValueType); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapperNotWriteNull.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import com.fasterxml.jackson.annotation.JsonInclude.Include; /** * Demonstrate how to using String as raw type when using RestTemplate to invoke a service that use POJO. e.g. *

* Provider:

* * public Response errorCodeWithHeader(MultiRequest request) * *

* Consumer:

* * String stringRequest = "{\"key\":\"testValue\"}"; * template.postForEntity(url, stringRequest, MultiResponse200.class); * *

* Caution: json will convert String to object based on String constructor, using this feature will make default * conversion change. You must write convertValue to check possible types using. */ public class RestObjectMapperWithStringMapperNotWriteNull extends RestObjectMapperWithStringMapper { private static final long serialVersionUID = 4279371572149490560L; public RestObjectMapperWithStringMapperNotWriteNull() { super(); setSerializationInclusion(Include.NON_NULL); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestMgr { private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class); private static final List errorList = new ArrayList<>(); private static String msg = ""; private static final AtomicLong checks = new AtomicLong(0); public static void setMsg(String msg) { TestMgr.msg = msg; } public static void setMsg(String microserviceName, String transport) { TestMgr.msg = String.format("microservice=%s, transport=%s", microserviceName, transport); } public static void check(Object expect, Object real) { check(expect, real, null); } public static void check(Object expect, Object real, Throwable error) { checks.incrementAndGet(); if (expect == real) { return; } String strExpect = String.valueOf(expect); String strReal = String.valueOf(real); if (!strExpect.equals(strReal)) { Error newError = new Error(msg + " | Expect " + strExpect + ", but " + strReal); if (error != null) { newError.setStackTrace(error.getStackTrace()); } errorList.add(newError); } } public static void checkNotEmpty(String real) { checks.incrementAndGet(); if (StringUtils.isEmpty(real)) { errorList.add(new Error(msg + " | unexpected null result, method is " + getCaller())); } } public static void fail(String desc) { failed(desc, new Exception(desc)); } public static void failed(String desc, Throwable e) { checks.incrementAndGet(); Error error = new Error(msg + " | " + desc + ", method is " + getCaller()); if (e != null) { error.setStackTrace(e.getStackTrace()); } errorList.add(error); } public static boolean isSuccess() { return errorList.isEmpty(); } public static void summary() { if (errorList.isEmpty()) { LOGGER.info("............. test finished ............"); LOGGER.info("............. total checks : " + checks.get()); return; } LOGGER.info("............. test not finished ............"); LOGGER.info("............. total checks : " + checks.get()); LOGGER.info("............. total errors : " + errorList.size()); LOGGER.info("............. error details: "); for (Throwable e : errorList) { LOGGER.info("", e); } } public static List errors() { return errorList; } private static String getCaller() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); if (stackTrace.length < 3) { return null; } StackTraceElement stackTraceElement = stackTrace[3]; return stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName(); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.api; import java.util.List; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.enums.ParameterStyle; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.Path; @Path("/headerList") public interface IHeaderParamWithListSchema { @Path("headerListDefault") @GET String headerListDefault( @HeaderParam("headerList") List headerList); @Path("headerListCSV") @GET String headerListCSV( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.FALSE) List headerList); @Path("headerListMULTI") @GET String headerListMULTI( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.TRUE) List headerList); @Path("headerListSSV") @GET String headerListSSV( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) List headerList); @Path("headerListPIPES") @GET String headerListPIPES( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) List headerList); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.api; import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.enums.ParameterStyle; @RequestMapping("/headerList") public interface IHeaderParamWithListSchemaSpringMvc { @GetMapping("headerListDefault") String headerListDefault( @RequestHeader("headerList") List headerList); @GetMapping("headerListCSV") String headerListCSV( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.FALSE) List headerList); @GetMapping("headerListMULTI") String headerListMULTI( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.TRUE) List headerList); @GetMapping("headerListSSV") String headerListSSV( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) List headerList); @GetMapping("headerListPIPES") String headerListPIPES( @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) List headerList); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Compute.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.compute; public interface Compute { int add(int a, int b); int reduce(int a, int b); Person sayHello(Person user); String testRawJsonString(String jsonInput); String saySomething(String prefix, Person user); void sayHi(String name); void sayHi2(String name); void sayHei(String name); boolean isTrue(); String addString(String[] s); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.compute; public class GenericParam { private String str; private long num; private T data; public String getStr() { return str; } public void setStr(String str) { this.str = str; } public GenericParam str(String str) { this.str = str; return this; } public long getNum() { return num; } public void setNum(long num) { this.num = num; } public GenericParam num(long num) { this.num = num; return this; } public T getData() { return data; } public void setData(T data) { this.data = data; } public GenericParam data(T data) { this.data = data; return this; } @Override public String toString() { StringBuilder sb = new StringBuilder("GenericParam{"); sb.append("str='").append(str).append('\''); sb.append(", num=").append(num); sb.append(", data=").append(data); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamExtended.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.compute; public class GenericParamExtended extends GenericParam { private String strExtended; private int intExtended; public String getStrExtended() { return strExtended; } public void setStrExtended(String strExtended) { this.strExtended = strExtended; } public GenericParamExtended strExtended(String strExtended) { this.strExtended = strExtended; return this; } public int getIntExtended() { return intExtended; } public void setIntExtended(int intExtended) { this.intExtended = intExtended; } public GenericParamExtended intExtended(int intExtended) { this.intExtended = intExtended; return this; } @Override public String toString() { StringBuilder sb = new StringBuilder("GenericParamExtended{"); sb.append("strExtended='").append(strExtended).append('\''); sb.append(", intExtended=").append(intExtended); sb.append(", super=").append(super.toString()); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamWithJsonIgnore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.compute; import com.fasterxml.jackson.annotation.JsonIgnore; public class GenericParamWithJsonIgnore { private String str; private long num; @JsonIgnore private T data; public String getStr() { return str; } public void setStr(String str) { this.str = str; } public GenericParamWithJsonIgnore str(String str) { this.str = str; return this; } public long getNum() { return num; } public void setNum(long num) { this.num = num; } public GenericParamWithJsonIgnore num(long num) { this.num = num; return this; } public T getData() { return data; } public void setData(T data) { this.data = data; } public GenericParamWithJsonIgnore data(T data) { this.data = data; return this; } @Override public String toString() { StringBuilder sb = new StringBuilder("GenericParamWithJsonIgnore{"); sb.append("str='").append(str).append('\''); sb.append(", num=").append(num); sb.append(", data=").append(data); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.compute; public class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return this.name; } public Person() { } public Person(String name) { this.name = name; } @Override public String toString() { return name; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Controller.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.controller; public interface Controller { int add(int a, int b); String sayHello(String name); String saySomething(String prefix, Person user); String sayHi(String name); String sayHei(String name); String sayHello1(String name); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.controller; public class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return this.name; } @Override public String toString() { return name; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.controller; public class PersonAlias { private String name; public void setName(String name) { this.name = name; } public String getName() { return this.name; } @Override public String toString() { return name; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/helloworld/greeter/Hello.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.helloworld.greeter; public interface Hello { String SayHello(String name); String SayHelloAgain(String name); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/IgnoreInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.ignore; public interface IgnoreInterface { void ignoreMethod(); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/InputModelForTestIgnore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.ignore; import com.fasterxml.jackson.annotation.JsonIgnore; import io.vertx.core.json.JsonObject; public class InputModelForTestIgnore { @JsonIgnore private String inputId = null; private String content = null; @JsonIgnore private Object inputObject = null; @JsonIgnore private JsonObject inputJsonObject = null; @JsonIgnore private IgnoreInterface inputIgnoreInterface = null; public String getInputId() { return this.inputId; } public void setInputId(String inputId) { this.inputId = inputId; } public String getContent() { return this.content; } public void setContent(String content) { this.content = content; } public Object getInputObject() { return inputObject; } public void setInputObject(Object inputObject) { this.inputObject = inputObject; } public JsonObject getInputJsonObject() { return inputJsonObject; } public void setInputJsonObject(JsonObject inputJsonObject) { this.inputJsonObject = inputJsonObject; } public IgnoreInterface getInputIgnoreInterface() { return inputIgnoreInterface; } public void setInputIgnoreInterface(IgnoreInterface inputIgnoreInterface) { this.inputIgnoreInterface = inputIgnoreInterface; } public InputModelForTestIgnore() { } public InputModelForTestIgnore(String inputId, String content, Object inputObject, JsonObject inputJsonObject, IgnoreInterface inputIgnoreInterface) { this.inputId = inputId; this.content = content; this.inputObject = inputObject; this.inputJsonObject = inputJsonObject; this.inputIgnoreInterface = inputIgnoreInterface; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/OutputModelForTestIgnore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.ignore; import com.fasterxml.jackson.annotation.JsonIgnore; import io.vertx.core.json.JsonObject; public class OutputModelForTestIgnore { @JsonIgnore private String outputId = null; private String inputId = null; private String content = null; @JsonIgnore private Object inputObject = null; @JsonIgnore private JsonObject inputJsonObject = null; @JsonIgnore private IgnoreInterface inputIgnoreInterface = null; @JsonIgnore private Object outputObject = null; @JsonIgnore private JsonObject outputJsonObject = null; @JsonIgnore private IgnoreInterface outputIgnoreInterface = null; public String getOutputId() { return this.outputId; } public void setOutputId(String outputId) { this.outputId = outputId; } public String getInputId() { return this.inputId; } public void setInputId(String inputId) { this.inputId = inputId; } public String getContent() { return this.content; } public void setContent(String content) { this.content = content; } public Object getInputObject() { return inputObject; } public void setInputObject(Object inputObject) { this.inputObject = inputObject; } public JsonObject getInputJsonObject() { return inputJsonObject; } public void setInputJsonObject(JsonObject inputJsonObject) { this.inputJsonObject = inputJsonObject; } public IgnoreInterface getInputIgnoreInterface() { return inputIgnoreInterface; } public void setInputIgnoreInterface(IgnoreInterface inputIgnoreInterface) { this.inputIgnoreInterface = inputIgnoreInterface; } public Object getOutputObject() { return outputObject; } public void setOutputObject(Object outputObject) { this.outputObject = outputObject; } public JsonObject getOutputJsonObject() { return outputJsonObject; } public void setOutputJsonObject(JsonObject outputJsonObject) { this.outputJsonObject = outputJsonObject; } public IgnoreInterface getOutputIgnoreInterface() { return outputIgnoreInterface; } public void setOutputIgnoreInterface(IgnoreInterface outputIgnoreInterface) { this.outputIgnoreInterface = outputIgnoreInterface; } public OutputModelForTestIgnore() { } public OutputModelForTestIgnore(String outputId, String inputId, String content, Object inputObject, JsonObject inputJsonObject, IgnoreInterface inputIgnoreInterface, Object outputObject, JsonObject outputJsonObject, IgnoreInterface outputIgnoreInterface) { this.outputId = outputId; this.inputId = inputId; this.content = content; this.inputObject = inputObject; this.inputJsonObject = inputJsonObject; this.inputIgnoreInterface = inputIgnoreInterface; this.outputObject = outputObject; this.outputJsonObject = outputJsonObject; this.outputIgnoreInterface = outputIgnoreInterface; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxbbean; import jakarta.xml.bind.annotation.XmlAccessOrder; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorOrder; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "job") @XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) public class JAXBJob { private String name; private String content; public JAXBJob() { } public JAXBJob(String name, String content) { this.name = name; this.content = content; } @Override public String toString() { return "Job{" + "name'" + name + '\'' + ", content='" + content + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxbbean; import java.io.Serializable; import jakarta.xml.bind.annotation.XmlAccessOrder; import jakarta.xml.bind.annotation.XmlAccessorOrder; import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; @XmlType(propOrder = {"name", "role", "job"}) @XmlRootElement(name = "person") @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) public class JAXBPerson implements Serializable { private static final long serialVersionUID = -7127275268696924681L; private String name; private int age; private String role; private String weight; private JAXBJob job; public JAXBPerson() { } public JAXBPerson(String name, int age, String role, String weight) { this.name = name; this.age = age; this.role = role; this.weight = weight; } public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlAttribute public int getAge() { return age; } public void setAge(int age) { this.age = age; } @XmlElement(nillable = true) public String getRole() { return role; } public void setRole(String role) { this.role = role; } @XmlTransient public String getWeight() { return weight; } public void setWeight(String weight) { this.weight = weight; } @XmlElement public JAXBJob getJob() { return job; } public void setJob(JAXBJob job) { this.job = job; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", role='" + role + '\'' + ", job=" + job + '}'; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.jaxrs.server.validation; import java.util.List; import jakarta.validation.constraints.NotNull; public class ValidationModel { @NotNull private Integer age; @NotNull private List members; @NotNull private String name; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public List getMembers() { return members; } public void setMembers(List members) { this.members = members; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.mapnull; import java.util.HashMap; import java.util.Map; public class ParseRequest { public String msgType = ""; public String strMsg; public String strID = ""; public Map flags = new HashMap<>(); public String getMsgType() { return msgType; } public void setMsgType(String msgType) { this.msgType = msgType; } public String getStrMsg() { return strMsg; } public void setStrMsg(String strMsg) { this.strMsg = strMsg; } public String getStrID() { return strID; } public void setStrID(String strID) { this.strID = strID; } public Map getFlags() { return flags; } public void setFlags(Map flags) { this.flags = flags; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.mapnull; import java.util.Map; public class ParseResponse { public String resultCode = "99999999"; public String resultInfo = "unknown result"; public String msgType = ""; public Map msgHeader; public Map msgBody; public String getResultCode() { return resultCode; } public void setResultCode(String resultCode) { this.resultCode = resultCode; } public String getResultInfo() { return resultInfo; } public void setResultInfo(String resultInfo) { this.resultInfo = resultInfo; } public String getMsgType() { return msgType; } public void setMsgType(String msgType) { this.msgType = msgType; } public Map getMsgHeader() { return msgHeader; } public void setMsgHeader(Map msgHeader) { this.msgHeader = msgHeader; } public Map getMsgBody() { return msgBody; } public void setMsgBody(Map msgBody) { this.msgBody = msgBody; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/model/SpecialNameModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.model; import com.fasterxml.jackson.annotation.JsonProperty; public class SpecialNameModel { // names starts with only one lower case , although getter/setter generated by IDE is correct, // will cause jackson generate incorrect swagger names. // @JsonProperty must be used to make json work in a predictable way. @JsonProperty("aIntName") private int aIntName; public int getaIntName() { return aIntName; } public void setaIntName(int aIntName) { this.aIntName = aIntName; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiErrorCode; public class MultiRequest{ private String message; private int code; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse200.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiErrorCode; public class MultiResponse200 { private String message; private int code; private long t200; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public long getT200() { return t200; } public void setT200(long t200) { this.t200 = t200; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse400.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiErrorCode; public class MultiResponse400 { private String message; private int code; private long t400; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public long getT400() { return t400; } public void setT400(long t400) { this.t400 = t400; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse500.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.multiErrorCode; public class MultiResponse500 { private String message; private int code; private long t500; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public long getT500() { return t500; } public void setT500(long t500) { this.t500 = t500; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.produceprocessor; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.demo.utils.JAXBUtils; import com.fasterxml.jackson.databind.JavaType; public class ProduceAppXmlProcessor implements ProduceProcessor { @Override public String getName() { return MediaType.APPLICATION_XML; } @Override public int getOrder() { return 0; } @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { output.write(JAXBUtils.convertToXml(result).getBytes(StandardCharsets.UTF_8)); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { return JAXBUtils.convertToJavaBean(input, type); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.produceprocessor.override; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.demo.utils.JAXBUtils; import com.fasterxml.jackson.databind.JavaType; public class ProduceAppXmlProcessor implements ProduceProcessor { @Override public String getName() { return MediaType.APPLICATION_XML; } @Override public int getOrder() { return -1; } @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { output.write(JAXBUtils.convertToXml(result).getBytes(StandardCharsets.UTF_8)); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { return JAXBUtils.convertToJavaBean(input, type); } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/AbstractModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; // 当使用 List 等 Collection 类型的时候, jackson 序列化不会携带类以外的其他属性 // 即会忽略掉类型信息。 当抽象类型用于 Collection 类型参数的时候,必须使用已有的属性(例子中的 property = "type")来确定类型, // 并在子类中显示指定属性的值,而不能使用和依赖jackson根据类型信息自己生成的类型。 // 还有一种方法,是序列化的时候指定类型, see: https://www.studytrails.com/2016/09/12/java-jackson-serialization-list/ // 但是多数序列化接口,包括 RestTemplate 等,都无法得知参数的类型。 这种方式难于应用于开发框架实现,因此对于Collection 类型参数场景,使用已有属性 // 是最简洁的方法。 @JsonTypeInfo( use = Id.NAME, property = "type" ) @JsonSubTypes({ @JsonSubTypes.Type(value = DefaultAbstractModel.class, name = "default"), @JsonSubTypes.Type(value = SecondAbstractModel.class, name = "second"), }) public abstract class AbstractModel { protected String type; protected String name; public abstract String getType(); public abstract String getName(); public abstract void setName(String name); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/DefaultAbstractModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; public class DefaultAbstractModel extends AbstractModel { @Override public String getType() { return "default"; } @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/GenericsModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import java.util.List; public class GenericsModel { private String name; private List> nameList; private List>> objectLists; public String getName() { return name; } public void setName(String name) { this.name = name; } public List> getNameList() { return nameList; } public void setNameList(List> nameList) { this.nameList = nameList; } public List>> getObjectLists() { return objectLists; } public void setObjectLists(List>> objectLists) { this.objectLists = objectLists; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/MapModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import java.util.Map; public class MapModel { private String name; private Map names; public String getName() { return name; } public void setName(String name) { this.name = name; } public Map getNames() { return names; } public void setNames(Map names) { this.names = names; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/NotRecommendedServiceInf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import java.util.List; import java.util.Map; public interface NotRecommendedServiceInf { Map longMap(Map map); List listAbstractModel(List listModel); AbstractModel abstractModel(AbstractModel model); Map mapAbstractModel(Map mapModel); WrappedAbstractModel wrappedAbstractModel(WrappedAbstractModel wrappedModel); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/SecondAbstractModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; public class SecondAbstractModel extends AbstractModel { @Override public String getType() { return "second"; } @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name = name; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; public interface Test { String testStringArray(String[] arr); String getTestString(String code); String postTestStatic(int code); String patchTestStatic(int code); String testException(int code); User wrapParam(TestRequest request); User splitParam(int index, User user); String addString(String[] strArr); String testTraceId(); int[] testIntArray(int[] request); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import java.util.ArrayList; import java.util.List; public class TestRequest { private int index; private User user; private List users = new ArrayList<>(); private byte[] data; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; public class TestResponse { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "TestResponse [user=" + user + "]"; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import com.fasterxml.jackson.core.JsonProcessingException; public class User { private String name = "nameA"; private int age = 100; private int index; private String[] names; public String getName() { return name; } public void setName(String name) { this.name = name; } public String[] getNames() { return names; } public void setNames(String[] names) { this.names = names; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; } public String jsonString() { try { return JsonUtils.writeValueAsString(this); } catch (JsonProcessingException e) { throw new IllegalStateException(e); } } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/WrappedAbstractModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.server; import java.util.List; import java.util.Map; public class WrappedAbstractModel { private String name; private Map mapModel; private List listModel; private AbstractModel model; public String getName() { return name; } public void setName(String name) { this.name = name; } public Map getMapModel() { return mapModel; } public void setMapModel(Map mapModel) { this.mapModel = mapModel; } public List getListModel() { return listModel; } public void setListModel(List listModel) { this.listModel = listModel; } public AbstractModel getModel() { return model; } public void setModel(AbstractModel model) { this.model = model; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Application.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.smartcare; import java.util.List; public class Application { private String name; private String labelEN; private String labelCH; private String defaultGroup; private String version; private boolean dynamicFlag; private List groups; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLabelEN() { return labelEN; } public void setLabelEN(String labelEN) { this.labelEN = labelEN; } public String getLabelCH() { return labelCH; } public void setLabelCH(String labelCH) { this.labelCH = labelCH; } public String getDefaultGroup() { return defaultGroup; } public void setDefaultGroup(String defaultGroup) { this.defaultGroup = defaultGroup; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public boolean isDynamicFlag() { return dynamicFlag; } public void setDynamicFlag(boolean dynamicFlag) { this.dynamicFlag = dynamicFlag; } public List getGroups() { return groups; } public void setGroups(List groups) { this.groups = groups; } @Override public String toString() { return "name=" + name + "\n" + "version=" + version; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Group.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.smartcare; public class Group { private String name; private String labelEN; private String labelCH; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLabelEN() { return labelEN; } public void setLabelEN(String labelEN) { this.labelEN = labelEN; } public String getLabelCH() { return labelCH; } public void setLabelCH(String labelCH) { this.labelCH = labelCH; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Response.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.smartcare; public class Response { private int resultCode; private String resultMessage; public int getResultCode() { return resultCode; } public void setResultCode(int resultCode) { this.resultCode = resultCode; } public String getResultMessage() { return resultMessage; } public void setResultMessage(String resultMessage) { this.resultMessage = resultMessage; } @Override public String toString() { return "resultCode: " + resultCode + "\n" + "resultMessage: " + resultMessage; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/SmartCare.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.smartcare; public interface SmartCare { Response addApplication(Application application); Response delApplication(String appName); } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.utils; import java.io.InputStream; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.sax.SAXSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.InputSource; import com.fasterxml.jackson.databind.JavaType; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.Marshaller; import jakarta.xml.bind.Unmarshaller; public class JAXBUtils { private static final Logger LOGGER = LoggerFactory.getLogger(JAXBUtils.class); private JAXBUtils() { } public static String convertToXml(Object obj) { return convertToXml(obj, StandardCharsets.UTF_8.toString()); } public static String convertToXml(Object obj, String encoding) { String result = null; try { JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding); StringWriter writer = new StringWriter(); marshaller.marshal(obj, writer); result = writer.toString(); } catch (Exception e) { LOGGER.error("Bean convert to xml failed, error message: {}", e.getMessage(), e); } return result; } @SuppressWarnings("unchecked") public static T convertToJavaBean(InputStream xml, JavaType type) { return (T) convertToJavaBean(xml, type.getRawClass()); } @SuppressWarnings("unchecked") public static T convertToJavaBean(InputStream xml, Class c) { T t = null; try { JAXBContext context = JAXBContext.newInstance(c); Unmarshaller unmarshaller = context.createUnmarshaller(); t = (T) unmarshaller.unmarshal(XXEPrevention(xml)); } catch (Exception e) { LOGGER.error("Xml convert to Bean failed, error message: {}", e.getMessage()); } return t; } private static Source XXEPrevention(InputStream xml) { Source xmlSource = null; SAXParserFactory spf = SAXParserFactory.newInstance(); try { spf.setFeature("http://xml.org/sax/features/external-general-entities", false); spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(xml)); } catch (Exception e) { LOGGER.error("Xml External Entity (XXE) Processing report error, error message: {}", e.getMessage()); } return xmlSource; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Student.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.validator; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.NotNull; public class Student { @NotNull private String name; @Max(20) private int age; public void setName(String name) { this.name = name; } public String getName() { return this.name; } public Student() { } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name + " " + age; } } ================================================ FILE: demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Teacher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.validator; import jakarta.validation.constraints.NotBlank; public class Teacher { @NotBlank(message = "must not be blank") private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return name + " " + age; } } ================================================ FILE: demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.produceprocessor.ProduceAppXmlProcessor org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor ================================================ FILE: demo/demo-schema/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: -1 demo.multi.service.center.serverB: key1: key1 key2: key2 key3: key3 servicecomb: # verbose exceptions information invocation: exception: print-stack-trace: true print-rate-limit: true ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml ================================================ 4.0.0 demo-spring-boot-pojo-client Java Chassis::Demo::Spring Boot::Transport::POJO Client org.apache.servicecomb.demo demo-spring-boot-transport 3.4.0-SNAPSHOT org.apache.servicecomb.demo.springboot.pojo.client.PojoClient docker demo-spring-boot-pojo-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/DemoConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.client; import org.apache.servicecomb.core.CoreConst; public interface DemoConst { String[] transports = new String[] {"rest", CoreConst.ANY_TRANSPORT}; } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.client; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ImportResource; /** * SpringmvcClient * * */ @SpringBootApplication @ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class PojoClient { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(PojoClient.class).web(WebApplicationType.SERVLET).build().run(args); PojoClientTest.runTest(); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.client; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.Test; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.TestRequest; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User; import org.apache.servicecomb.provider.pojo.RpcReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class PojoClientTest { private static Logger LOGGER = LoggerFactory.getLogger(PojoClientTest.class); @RpcReference(microserviceName = "spring-boot-pojo-server", schemaId = "server") public static Test test; private static Test testFromXml; public static final byte[] buffer = new byte[1024]; public static final String SPLITPARAM_RESPONSE_USER_SUFFIX = "(modified by MyHandler)"; static { Arrays.fill(buffer, (byte) 1); } public static void setTestFromXml(Test testFromXml) { PojoClientTest.testFromXml = testFromXml; } public static void runTest() throws Exception { String microserviceName = "spring-boot-pojo-server"; for (String transport : DemoConst.transports) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); LOGGER.info("test {}, transport {}", microserviceName, transport); testNull(testFromXml); testNull(test); testEmpty(test); testStringArray(test); testChinese(test); testStringHaveSpace(test); testWrapParam(test); testSplitParam(test); testInputArray(test); testCommonInvoke(transport); } TestMgr.summary(); } private static void testInputArray(Test test) { String result = test.addString(new String[] {"a", "b"}); LOGGER.info("input array result:{}", result); TestMgr.check("[a, b]", result); } private static void testSplitParam(Test test) { User result = test.splitParam(1, new User()); LOGGER.info("split param result:{}", result); TestMgr.check("User [name=nameA, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX + ", age=100, index=1]", result); } @SuppressWarnings({"deprecation"}) private static void testCommonInvoke(String transport) { Map arguments = new HashMap<>(); Map wrap = new HashMap<>(); arguments.put("index", 2); arguments.put("user", new User()); wrap.put("splitParamBody", arguments); Object result = InvokerUtils.syncInvoke("spring-boot-pojo-server", "server", "splitParam", wrap, User.class); TestMgr.check("User [name=nameA, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX + ", age=100, index=2]", result); arguments = new HashMap<>(); arguments.put("index", 3); arguments.put("user", new User()); wrap = new HashMap<>(); wrap.put("splitParamBody", arguments); result = InvokerUtils.syncInvoke("spring-boot-pojo-server", transport, "server", "splitParam", wrap, User.class); TestMgr.check("User [name=nameA, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX + ", age=100, index=3]", result); } private static void testEmpty(Test test) { TestMgr.check("code is ''", test.getTestString("")); } private static void testNull(Test test) { TestMgr.check("code is 'null'", test.getTestString(null)); TestMgr.check(null, test.wrapParam(null)); } private static void testChinese(Test test) { TestMgr.check("code is '测试'", test.getTestString("测试")); User user = new User(); user.setName("名字"); User result = test.splitParam(1, user); TestMgr.check("名字, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX, result.getName()); } private static void testStringHaveSpace(Test test) { TestMgr.check("code is 'a b'", test.getTestString("a b")); } private static void testStringArray(Test test) { // TestMgr.check("arr is '[a, , b]'", test.testStringArray(new String[] {"a", null, "b"})); TestMgr.check("arr is '[a, b]'", test.testStringArray(new String[] {"a", "b"})); } private static void testWrapParam(Test test) { User user = new User(); TestRequest request = new TestRequest(); request.setUser(user); request.setIndex(0); request.setData(buffer); request.getUsers().add(user); User result = test.wrapParam(request); LOGGER.info("wrap param result:{}", result); TestMgr.check("User [name=nameA, users count:1, age=100, index=0]", result); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/TestMgr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.client; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestMgr { private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class); private static final List errorList = new ArrayList<>(); private static String msg = ""; public static void setMsg(String msg) { TestMgr.msg = msg; } public static void setMsg(String microserviceName, String transport) { TestMgr.msg = String.format("microservice=%s, transport=%s", microserviceName, transport); } public static void check(Object expect, Object real) { String strExpect = String.valueOf(expect); String strReal = String.valueOf(real); if (!strExpect.equals(strReal)) { errorList.add(new Error(msg + " | Expect " + strExpect + ", but " + strReal)); } } public static void summary() { if (errorList.isEmpty()) { LOGGER.info("............. test finished ............"); return; } LOGGER.info("............. test not finished ............"); for (Throwable e : errorList) { LOGGER.info("", e); } } public static List errors() { return errorList; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; public interface Test { String testStringArray(String[] arr); String getTestString(String code); String postTestStatic(int code); String testException(int code); User wrapParam(TestRequest request); User splitParam(int index, User user); String addString(String[] strArr); } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; import java.util.ArrayList; import java.util.List; public class TestRequest { private int index; private User user; private List users = new ArrayList<>(); private byte[] data; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; public class TestResponse { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "TestResponse [user=" + user + "]"; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; public class User { private String name = "nameA"; private int age = 100; private int index; private String[] names; public String getName() { return name; } public void setName(String name) { this.name = name; } public String[] getNames() { return names; } public void setNames(String[] names) { this.names = names; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml ================================================ ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8082 servicecomb: service: application: spring-boot-pojotest name: spring-boot-pojo-client version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 handler: chain: Consumer: default: loadbalance isolation: Consumer: enabled: false references: pojo: version-rule: 0.0.1 ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.client; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = PojoClient.class) public class PojoClientIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { PojoClientTest.runTest(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml ================================================ 4.0.0 demo-spring-boot-pojo-server Java Chassis::Demo::Spring Boot::Transport::POJO Server org.apache.servicecomb.demo demo-spring-boot-transport 3.4.0-SNAPSHOT org.apache.servicecomb.demo.springboot.pojo.server.PojoServer jakarta.ws.rs jakarta.ws.rs-api org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/PojoServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class PojoServer { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(PojoServer.class).web(WebApplicationType.SERVLET).build().run(args); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.handler; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class MyHandler extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class); public static final String SPLITPARAM_RESPONSE_USER_SUFFIX = "(modified by MyHandler)"; @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 100; } @Override public String getName() { return "test-my-filter"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { LOGGER.info("If you see this log, that means this demo project has been converted to ServiceComb framework."); return nextNode.onFilter(invocation).whenComplete((response, throwable) -> { if (invocation.getOperationMeta().getSchemaQualifiedName().equals("server.splitParam")) { User user = response.getResult(); user.setName(user.getName() + SPLITPARAM_RESPONSE_USER_SUFFIX); } }); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/greeter/Hello.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.greeter; public interface Hello { String SayHello(String name); String SayHelloAgain(String name); } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/HelloImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; import org.apache.servicecomb.demo.springboot.pojo.server.schema.greeter.Hello; import org.apache.servicecomb.provider.pojo.RpcSchema; @RpcSchema(schemaId = "helloworld.Greeter") public class HelloImpl implements Hello { @Override public String SayHello(String name) { return "Hello Message fast"; } @Override public String SayHelloAgain(String name) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello Message slow"; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; public interface Test { String testStringArray(String[] arr); String getTestString(String code); String postTestStatic(int code); String testException(int code); User wrapParam(TestRequest request); User splitParam(int index, User user); String addString(String[] strArr); } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @RpcSchema(schemaId = "server") public class TestImpl implements Test { @Override public String testStringArray(String[] arr) { return String.format("arr is '%s'", Arrays.toString(arr)); } @Override public String getTestString(String code) { return String.format("code is '%s'", code); } @Override public String postTestStatic(int code) { return null; } private User doTest(int index, User user, List users, byte[] data) { if (user == null) { user = new User(); } user.setIndex(index); int userCount = (users == null) ? 0 : users.size(); user.setName(user.getName() + ", users count:" + userCount); return user; } @Override public String testException(int code) { String strCode = String.valueOf(code); switch (code) { case 200: return strCode; case 456: throw new InvocationException(code, strCode, strCode + " error"); case 556: throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); case 557: throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); default: break; } return "not expected"; } @Override public User splitParam(int index, User user) { return doTest(index, user, null, null); } @Override public User wrapParam(TestRequest request) { if (request == null) { return null; } return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); } @Override public String addString(String[] strArr) { String result = Arrays.toString(strArr); System.out.println("addString: " + result); return result; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; import java.util.ArrayList; import java.util.List; public class TestRequest { private int index; private User user; private List users = new ArrayList<>(); private byte[] data; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; public class TestResponse { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "TestResponse [user=" + user + "]"; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springboot.pojo.server.schema.server; public class User { private String name = "nameA"; private int age = 100; private int index; private String[] names; public String getName() { return name; } public void setName(String name) { this.name = name; } public String[] getNames() { return names; } public void setNames(String[] names) { this.names = names; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8080 servicecomb: service: application: spring-boot-pojotest name: spring-boot-pojo-server version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml ================================================ 4.0.0 demo-spring-boot-springmvc-client Java Chassis::Demo::Spring Boot::Spring MVC Client org.apache.servicecomb.demo demo-spring-boot-transport 3.4.0-SNAPSHOT org.apache.servicecomb.demo demo-schema docker demo-spring-boot-springmvc-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/PlaceHolderSchemaTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class PlaceHolderSchemaTest implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { RestOperations template = RestTemplateBuilder.create(); String result = template.getForObject("servicecomb://springmvc/placeholder/schema?name=test", String.class); TestMgr.check("test", result); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.springboot.springmvc.client.ThirdSvcConfiguration.ReactiveStreamClient; import org.apache.servicecomb.springboot.springmvc.client.ThirdSvcConfiguration.ReactiveStreamClient.Model; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class ReactiveStreamIT implements CategorizedTestCase { @Autowired @Qualifier("reactiveStreamProvider") ReactiveStreamClient reactiveStreamProvider; @Override public void testRestTransport() throws Exception { testSseString(reactiveStreamProvider); testSseModel(reactiveStreamProvider); } private void testSseModel(ReactiveStreamClient client) throws Exception { Publisher result = client.sseModel(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(Model s) { buffer.append(s.getName()).append(s.getAge()); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); } private void testSseString(ReactiveStreamClient client) throws Exception { Publisher result = client.sseString(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(String s) { buffer.append(s); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("abc", buffer.toString()); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; /** * SpringMvcClient * * */ @SpringBootApplication public class SpringMvcClient { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(SpringMvcClient.class).web(WebApplicationType.SERVLET).build().run(args); runTest(); } public static void runTest() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("springmvc"); TestMgr.summary(); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import org.apache.http.HttpStatus; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class TestAnnotationsSchema implements CategorizedTestCase { private static final String microserviceName = "springmvc"; private static final RestTemplate templateUrlWithServiceName = new CseRestTemplate(); @Override public void testRestTransport() throws Exception { templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); testRequiredBody(templateUrlWithServiceName, microserviceName); testRegExpPath(); } private void testRegExpPath() { String prefix = "cse://" + microserviceName; String result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a?name={name}", String.class, "a"); TestMgr.check("a", result); result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b?name={name}", String.class, "ab"); TestMgr.check("ab", result); result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b/c?name={name}", String.class, "abc"); TestMgr.check("abc", result); } private static void testRequiredBody(RestTemplate template, String microserviceName) { String prefix = "cse://" + microserviceName; Person user = new Person(); TestMgr.check("No user data found", template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", user, String.class, "ha")); user.setName("world"); TestMgr.check("ha world", template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", user, String.class, "ha")); TestMgr.check("No user data found", template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", null, String.class, "ha")); TestMgr.check("No user name found", template.postForObject(prefix + "/annotations/say", "", String.class, "ha")); TestMgr.check("test", template.postForObject(prefix + "/annotations/say", "test", String.class, "ha")); try { template.postForObject(prefix + "/annotations/testRequiredBody", null, String.class); TestMgr.fail("should fail"); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import org.apache.servicecomb.provider.pojo.Invoker; import org.reactivestreams.Publisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @Configuration public class ThirdSvcConfiguration { @RequestMapping(path = "/") public interface ReactiveStreamClient { class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") Publisher sseString(); @GetMapping("/sseModel") Publisher sseModel(); } @Bean("reactiveStreamProvider") public ReactiveStreamClient reactiveStreamProvider() { return Invoker.createProxy("springmvc", "ReactiveStreamController", ReactiveStreamClient.class); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import java.io.File; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class UploadDownloadSchemaTest implements CategorizedTestCase { RestOperations restOperations = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { testEmptyFileUploadWork(); testNonEmptyFileUploadWork(); } private void testNonEmptyFileUploadWork() throws Exception { String file2Content = " bonjour"; File someFile = File.createTempFile("upload2", ".txt"); FileUtils.writeStringToFile(someFile, file2Content, StandardCharsets.UTF_8, false); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); Map params = new HashMap<>(); params.put("name", "test"); params.put("file", someFile); HttpEntity> entity = new HttpEntity<>(params, headers); String url = "servicecomb://springmvc/up/down/upload"; String result = restOperations.postForObject(url, entity, String.class); TestMgr.check("test; bonjour", result); } private void testEmptyFileUploadWork() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); Map params = new HashMap<>(); params.put("name", "test"); HttpEntity> entity = new HttpEntity<>(params, headers); String url = "servicecomb://springmvc/up/down/upload"; String result = restOperations.postForObject(url, entity, String.class); TestMgr.check("test;", result); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8999 servicecomb: service: application: springmvcboottest name: springmvcclient version: 0.0.3 registry: sc: address: http://127.0.0.1:30100 test: vert: transport: false ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: 100 3rd-svc: urls: - rest://localhost:8080?sslEnabled=false ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/test/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClientIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.client; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = SpringMvcClient.class) public class SpringMvcClientIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { try { SpringMvcClient.runTest(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } catch (Throwable e) { e.printStackTrace(); Assertions.fail("test case failed, message=" + e.getMessage()); } } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml ================================================ 4.0.0 demo-spring-boot-springmvc-server Java Chassis::Demo::Spring Boot::Spring MVC Server org.apache.servicecomb.demo demo-spring-boot-transport 3.4.0-SNAPSHOT org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.server; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "annotations") @RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) public class AnnotationsSchema { @GetMapping(path = "/add") public int add(@RequestParam(name = "a", defaultValue = "10") int a, @RequestParam(name = "b", defaultValue = "10") int b) { return a + b; } @RequestMapping(path = "/sayhei", method = RequestMethod.GET) public String sayHei(@RequestHeader(name = "name", defaultValue = "test") String name) { return "hei " + name; } @GetMapping(path = "/sayhi") @Parameters({ @Parameter(name = "name", in = ParameterIn.QUERY, schema = @Schema(type = "string", defaultValue = "test")), @Parameter(name = "age", in = ParameterIn.QUERY, schema = @Schema(type = "integer", defaultValue = "20")) }) public String sayHi(String name, int age) { return "hi " + name + " your age is : " + age; } @RequestMapping(path = "/saysomething", method = RequestMethod.POST) public String saySomething(String prefix, @RequestBody(required = false) Person user) { if (user == null || user.getName() == null || user.getName().isEmpty()) { return "No user data found"; } return prefix + " " + user.getName(); } @RequestMapping(path = "/say", method = RequestMethod.POST) public String say(@RequestBody(required = false) String user) { if (user == null || user.isEmpty()) { return "No user name found"; } return user; } @RequestMapping(path = "/testRequiredBody", method = RequestMethod.POST) public String testRequiredBody(@RequestBody(required = true) Person user) { if (user == null) { return "Should not happen"; } return user.getName(); } @RequestMapping(path = "/testRegExpPath/{path: .+}", method = RequestMethod.GET) public String testRegExpPath(@RequestParam("name") String name) { return name; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/PlaceHolderSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "PlaceHolderSchema") @RequestMapping(path = "${test.placeholder.schema}") public class PlaceHolderSchema { @GetMapping(path = "/") public String root(@RequestParam("name") String name) { return name; } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.server; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.reactivex.rxjava3.core.Flowable; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") public class ReactiveStreamController { public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") public Publisher sseString() { return Flowable.fromArray("a", "b", "c"); } @GetMapping("/sseModel") public Publisher sseModel() { return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) .map(item -> new Model("jack", item.intValue())); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/SpringmvcServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.server; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication(exclude = {WebMvcAutoConfiguration.class}) public class SpringmvcServer { private static final Logger LOGGER = LoggerFactory.getLogger(SpringmvcServer.class); public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(SpringmvcServer.class).web(WebApplicationType.SERVLET).build().run(args); assertPropertyCorrect(); } private static void assertPropertyCorrect() { // spring environment will fail for unresolved placeholder property try { LegacyPropertyFactory.getStringProperty("test.unresolved.placeholder"); } catch (IllegalArgumentException e) { return; } LOGGER.error("tests for configuration error, stop"); SCBEngine.getInstance().destroy(); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.springmvc.server; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "UploadDownloadSchema") @RequestMapping(path = "/up/down") public class UploadDownloadSchema { @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA) public String fileUpload(@RequestPart MultipartFile file, @RequestPart(value = "name") String name) throws Exception { StringBuilder result = new StringBuilder(); result.append(name).append(";"); if (file == null || file.isEmpty()) { return result.toString(); } try (InputStream is = file.getInputStream()) { result.append(IOUtils.toString(is, StandardCharsets.UTF_8)); } return result.toString(); } } ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8080 test.unresolved.placeholder: jdbc:postgresql://${ip}:${port}/pt test: placeholder: schema: placeholder/schema servicecomb: service: application: springmvcboottest name: springmvc version: 0.0.3 registry: sc: address: http://127.0.0.1:30100 test: vert: transport: false ================================================ FILE: demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: config: ignoreResolveFailure: true service: registry: registerPath: true address: http://127.0.0.1:9980,http://127.0.0.1:30100 client: timeout: idle: 60 watch: 60 instance: healthCheck: interval: 3 pull: interval: 10 watch: true autodiscovery: true uploads: directory: target rest: address: 0.0.0.0:8080?sslEnabled=false ================================================ FILE: demo/demo-spring-boot-transport/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-spring-boot-transport Java Chassis::Demo::Spring Boot::Transport pom demo-spring-boot-springmvc-server demo-spring-boot-springmvc-client demo-spring-boot-pojo-server demo-spring-boot-pojo-client org.apache.servicecomb solution-basic org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.springframework.boot spring-boot-starter-logging org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core org.apache.servicecomb registry-service-center org.apache.servicecomb foundation-test-scaffolding compile ================================================ FILE: demo/demo-springmvc/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-springmvc Java Chassis::Demo::Spring MVC pom springmvc-server springmvc-client org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-service-center org.apache.servicecomb registry-local org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-springmvc/springmvc-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-springmvc 3.4.0-SNAPSHOT springmvc-client Java Chassis::Demo::Spring MVC::Client org.apache.servicecomb.demo.springmvc.SpringmvcClient docker springmvc-server io.fabric8 docker-maven-plugin servicecomb/service-center service-center server is ready 30100 30100:30100 ${demo.service.name}:${project.version} ${demo.service.name} -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 /maven/maven/${demo.service.name}-${project.version}.jar service-center:sc.servicecomb.io ServiceComb is ready 8080 7070:7070 8080:8080 service-center start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpStatus; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.demo.springmvc.client.CodeFirstRestTemplateSpringmvc; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.provider.springmvc.reference.UrlWithProviderPrefixClientHttpRequestFactory; import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ImportResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @SpringBootApplication @ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") public class SpringmvcClient { private static final Logger LOGGER = LoggerFactory.getLogger(SpringmvcClient.class); private static RestTemplate templateUrlWithServiceName = new CseRestTemplate(); private static RestTemplate templateUrlWithProviderPrefix = new CseRestTemplate(); private static RestOperations restTemplate; public static void main(String[] args) { new SpringApplicationBuilder(SpringmvcClient.class).web(WebApplicationType.NONE).run(args); run(); } private static void changeTransport(String microserviceName, String transport) { InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); TestMgr.setMsg(microserviceName, transport); } public static void run() { try { runImpl(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); LOGGER.info("-------------- last time updated checks(maybe more/less): 1344 -------------"); } private static void runImpl() throws Exception { testHttpClientsIsOk(); templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); restTemplate = RestTemplateBuilder.create(); templateUrlWithProviderPrefix.setRequestFactory(new UrlWithProviderPrefixClientHttpRequestFactory("/pojo/rest")); String prefix = "cse://springmvc"; String microserviceName = "springmvc"; try { // only works in rest, highway does not have name param, because not defined. templateUrlWithServiceName.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); TestMgr.check("true", "false"); } catch (Exception e) { TestMgr.check("true", "true"); } try { // only works in rest, highway does not have name param, because not defined. templateUrlWithServiceName.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); TestMgr.check("true", "false"); } catch (Exception e) { TestMgr.check("true", "true"); } testHandler(microserviceName); CodeFirstRestTemplateSpringmvc codeFirstClient = BeanUtils.getContext().getBean(CodeFirstRestTemplateSpringmvc.class); codeFirstClient.testCodeFirst(restTemplate, "springmvc", "/codeFirstSpringmvc/"); codeFirstClient.testCodeFirst(templateUrlWithProviderPrefix, "springmvc", "/pojo/rest/codeFirstSpringmvc/"); testAllTransport(microserviceName); testRestTransport(microserviceName, prefix); CategorizedTestCaseRunner.runCategorizedTestCase(microserviceName); } private static void testHandler(String microserviceName) { changeTransport(microserviceName, "rest"); String prefix = "cse://springmvc"; String result = templateUrlWithServiceName.getForObject(prefix + "/controller/sayHello1?name=tom", String.class); TestMgr.check("Hello tom,v", result); } private static void testHttpClientsIsOk() { TestMgr.check(HttpClients.getClient("http-transport-client") != null, true); TestMgr.check(HttpClients.getClient("http2-transport-client") != null, true); TestMgr.check(HttpClients.getClient("http-transport-client", false) != null, true); TestMgr.check(HttpClients.getClient("http2-transport-client", false) != null, true); } private static void testRestTransport(String microserviceName, String prefix) { changeTransport(microserviceName, "rest"); testControllerRest(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesRest(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesJavaPrimitiveRest(templateUrlWithServiceName, microserviceName); HttpHeaders headers = new HttpHeaders(); headers.set("Accept-Encoding", "gzip"); HttpEntity entity = new HttpEntity<>(headers); ResponseEntity entityCompress = restTemplate.exchange(prefix + "/codeFirstSpringmvc/sayhi/compressed/{name}/v2", HttpMethod.GET, entity, String.class, "Test"); TestMgr.check( "Test sayhi compressed:This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text!", entityCompress.getBody()); // if server response is compressed, the content-length header will be removed , so can't check this. // the transfer-encoding header will be missing when the server is set to not compressed if (entityCompress.getHeaders().get("transfer-encoding") != null) { TestMgr.check("chunked", entityCompress.getHeaders().get("transfer-encoding").get(0)); } //0.5.0 later version metrics integration test @SuppressWarnings("unchecked") Map metrics = restTemplate.getForObject(prefix + "/scb/metrics", Map.class); // TestMgr.check(true, metrics.get("jvm(name=heapUsed,statistic=gauge)") != 0); TestMgr.check(true, metrics.size() > 0); //prometheus integration test try { String content = restTemplate .getForObject("cse://springmvc/codeFirstSpringmvc/prometheusForTest", String.class); String application = LegacyPropertyFactory.getStringProperty("servicecomb.service.application", ""); TestMgr.check(true, content.contains( "servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.addDate")); TestMgr.check(true, content.contains( "servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.sayHello")); TestMgr.check(true, content .contains("servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.isTrue")); TestMgr.check(true, content.contains("servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.add")); TestMgr.check(true, content .contains("servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.sayHi2")); TestMgr.check(true, content .contains( "servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.saySomething")); String[] metricLines = content.split("\n"); if (metricLines.length > 0) { for (String metricLine : metricLines) { if (!metricLine.startsWith("#")) { String[] metricKeyAndValue = metricLine.split(" "); if (!metricKeyAndValue[0].startsWith("jvm") && !metricKeyAndValue[0].startsWith("os")) { if (Double.parseDouble(metricKeyAndValue[1]) < 0) { TestMgr.check("true", "false"); break; } } } } } else { TestMgr.check("true", "false"); } } catch (Exception e) { LOGGER.error("", e); TestMgr.check("true", "false"); } } private static void testAllTransport(String microserviceName) { for (String transport : DemoConst.transports) { changeTransport(microserviceName, transport); TestMgr.setMsg(microserviceName, transport); testControllerAllTransport(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesAllTransport(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesJavaPrimitiveAllTransport(templateUrlWithServiceName, microserviceName); } } private static void testControllerRest(RestTemplate template, String microserviceName) { String prefix = "cse://" + microserviceName; TestMgr.check("hi world [world]", template.getForObject(prefix + "/controller/sayhi?name=world", String.class)); TestMgr.check("hi world boot [world boot]", template.getForObject(prefix + "/controller/sayhi?name=world boot", String.class)); TestMgr.check("hi world boot [world boot]", template.getForObject(prefix + "/controller/sayhi?name=world+boot", String.class)); TestMgr.check("hi world1 [world1]", template.getForObject(prefix + "/controller/sayhi?name={name}", String.class, "world1")); TestMgr.check("hi world1+world2 [world1+world2]", template.getForObject(prefix + "/controller/sayhi?name={name}", String.class, "world1+world2")); TestMgr.check("hi hi 中国 [hi 中国]", template.getForObject(prefix + "/controller/sayhi?name={name}", String.class, "hi 中国")); Map params = new HashMap<>(); params.put("name", "world2"); TestMgr.check("hi world2 [world2]", template.getForObject(prefix + "/controller/sayhi?name={name}", String.class, params)); try { template.postForObject(prefix + "/controller/sayhello/{name}", null, String.class, "exception"); TestMgr.check(true, false); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 503); } } private static void testControllerAllTransport(RestTemplate template, String microserviceName) { String prefix = "cse://" + microserviceName; TestMgr.check(7, template.getForObject(prefix + "/controller/add?a=3&b=4", Integer.class)); try { template.getForObject(prefix + "/controller/add", Integer.class); TestMgr.check("failed", "success"); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); } TestMgr.check("hello world", template.postForObject(prefix + "/controller/sayhello/{name}", null, String.class, "world")); TestMgr.check("hello hello 中国", template.postForObject(prefix + "/controller/sayhello/{name}", null, String.class, "hello 中国")); try { template.postForObject(prefix + "/controller/sayhello/{name}", null, String.class, "exception"); TestMgr.check(true, false); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 503); } HttpHeaders headers = new HttpHeaders(); headers.add("name", "world"); @SuppressWarnings("rawtypes") HttpEntity entity = new HttpEntity<>(null, headers); ResponseEntity response = template.exchange(prefix + "/controller/sayhei", HttpMethod.GET, entity, String.class); TestMgr.check("hei world", response.getBody()); Person user = new Person(); user.setName("world"); TestMgr.check("ha world", template.postForObject(prefix + "/controller/saysomething?prefix={prefix}", user, String.class, "ha")); } private static void testSpringMvcDefaultValuesRest(RestTemplate template, String microserviceName) { String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; String result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); TestMgr.check("Hello 20bobo4010", result); boolean failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); TestMgr.check("Hello 20bobo40302", result); failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); } private static void testSpringMvcDefaultValuesAllTransport(RestTemplate template, String microserviceName) { String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; //default values HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); HttpEntity> request = new HttpEntity<>(map, headers); String result = template.postForObject(cseUrlPrefix + "/form", request, String.class); TestMgr.check("Hello 20bobo", result); headers = new HttpHeaders(); HttpEntity entity = new HttpEntity<>(null, headers); result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); TestMgr.check("Hello 20bobo30", result); result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); TestMgr.check("Hello 20bobo4010", result); boolean failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); TestMgr.check("Hello 20bobo40302", result); failed = false; result = null; try { result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); } catch (InvocationException e) { failed = true; TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } TestMgr.check(true, failed); TestMgr.check(null, result); result = template.getForObject(cseUrlPrefix + "/query3?a=30&b=2", String.class); TestMgr.check("Hello 302", result); result = template.getForObject(cseUrlPrefix + "/query3?a=30", String.class); TestMgr.check("Hello 30null", result); //input values headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntity> requestPara = new HttpEntity<>(null, headers); result = template.postForObject(cseUrlPrefix + "/form?a=30&b=sam", requestPara, String.class); TestMgr.check("Hello 30sam", result); headers = new HttpHeaders(); headers.add("a", "30"); headers.add("b", "sam"); headers.add("c", "40"); entity = new HttpEntity<>(null, headers); result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); TestMgr.check("Hello 30sam40", result); result = template.getForObject(cseUrlPrefix + "/query?a=3&b=sam&c=5&d=30", String.class); TestMgr.check("Hello 3sam530", result); result = template.getForObject(cseUrlPrefix + "/query2?a=3&b=4&c=5&d=30&e=2", String.class); TestMgr.check("Hello 345302", result); } private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestTemplate template, String microserviceName) { String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; //default values with primitive String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", null, String.class); TestMgr.check("Hello 0bobo", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", null, String.class); TestMgr.check("Hello 0.0false", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", null, String.class); TestMgr.check("Hello", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", null, String.class); TestMgr.check("Hello nullnull", result); result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); result = template.postForObject(cseUrlPrefix + "/allprimitivetypes?pBoolean=true&pChar=c&pByte=20&pShort=30&pInt=40&pLong=50&pFloat=60&pDouble=70&pDoubleWrap=80", null, String.class); TestMgr.check("Hello true,c,20,30,40,50,60.0,70.0,80.0", result); } private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestTemplate template, String microserviceName) { String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; //default values with primitive String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", null, String.class); TestMgr.check("Hello 0bobo", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", null, String.class); TestMgr.check("Hello 0.0false", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", null, String.class); TestMgr.check("Hello", result); result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", null, String.class); TestMgr.check("Hello nullnull", result); result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.CodeFirstRestTemplate; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import jakarta.servlet.http.Part; @Component public class CodeFirstRestTemplateSpringmvc extends CodeFirstRestTemplate { interface UploadPartAndFile { String fileUpload(Part file1, File someFile); } interface UploadStreamAndResource { String fileUpload(InputStream file1, Resource someFile); } private UploadPartAndFile uploadPartAndFile = Invoker.createProxy("springmvc", "codeFirst", UploadPartAndFile.class); private UploadStreamAndResource uploadStreamAndResource = Invoker.createProxy("springmvc", "codeFirst", UploadStreamAndResource.class); private TestResponse testResponse = new TestResponse(); private TestObject testObject = new TestObject(); private TestGeneric testGeneric = new TestGeneric(); private TestRestTemplate testRestTemplate = new TestRestTemplate(); private TestContentType testContentType = new TestContentType(); @Autowired public void setEnvironment(Environment environment) { this.environment = environment; testResponse.setEnvironment(environment); } @Override protected void testOnlyRest(String microservcieName, RestOperations template, String cseUrlPrefix) { try { testUpload(template, cseUrlPrefix); } catch (IOException e) { throw new IllegalStateException(e); } testResponseEntity("springmvc", template, cseUrlPrefix); testCodeFirstTestFormRest(template, cseUrlPrefix); testResponse.runRest(); testObject.runRest(); testGeneric.runRest(); testRestTemplate.runRest(); testContentType.runAllTest(); super.testOnlyRest(microservcieName, template, cseUrlPrefix); } @Override protected void testOnlyHighway(RestOperations template, String cseUrlPrefix) { testResponse.runHighway(); testObject.runHighway(); testGeneric.runHighway(); testCodeFirstTestFormHighway(template, cseUrlPrefix); super.testOnlyHighway(template, cseUrlPrefix); } @Override protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { testResponse.runAllTransport(); testObject.runAllTransport(); testGeneric.runAllTransport(); testRestTemplate.runAllTest(); testResponseEntity("springmvc", template, cseUrlPrefix); testCodeFirstTestForm(template, cseUrlPrefix); super.testAllTransport(microserviceName, template, cseUrlPrefix); } private void testUpload(RestOperations template, String cseUrlPrefix) throws IOException { String file1Content = "hello world"; File file1 = File.createTempFile("测 试", ".txt"); FileUtils.writeStringToFile(file1, file1Content, StandardCharsets.UTF_8, false); String file2Content = " bonjour"; File someFile = File.createTempFile("upload2", ".txt"); FileUtils.writeStringToFile(someFile, file2Content, StandardCharsets.UTF_8, false); String expect = String.format("%s:%s:%s\n" + "%s:%s:%s", file1.getName(), MediaType.TEXT_PLAIN_VALUE, file1Content, someFile.getName(), MediaType.TEXT_PLAIN_VALUE, file2Content); String result = testRestTemplateUpload(template, cseUrlPrefix, file1, someFile); TestMgr.check(expect, result); result = uploadPartAndFile.fileUpload(new FilePart(null, file1), someFile); TestMgr.check(expect, result); expect = "hello world"; MultiValueMap map = new LinkedMultiValueMap<>(); map.add("file1", new FileSystemResource(file1)); result = template.postForObject( cseUrlPrefix + "/upload1", new HttpEntity<>(map), String.class); TestMgr.check(expect, result); expect = String.format("null:%s:%s\n" + "%s:%s:%s", MediaType.APPLICATION_OCTET_STREAM_VALUE, file1Content, someFile.getName(), MediaType.TEXT_PLAIN_VALUE, file2Content); result = uploadStreamAndResource .fileUpload(new ByteArrayInputStream(file1Content.getBytes(StandardCharsets.UTF_8)), new org.springframework.core.io.PathResource(someFile.getAbsolutePath())); TestMgr.check(expect, result); } private String testRestTemplateUpload(RestOperations template, String cseUrlPrefix, File file1, File someFile) { MultiValueMap map = new LinkedMultiValueMap<>(); map.add("file1", new FileSystemResource(file1)); map.add("someFile", new FileSystemResource(someFile)); return template.postForObject( cseUrlPrefix + "/upload", new HttpEntity<>(map), String.class); } private void testResponseEntity(String microserviceName, RestOperations template, String cseUrlPrefix) { Map body = new HashMap<>(); Date date = new Date(); body.put("date", date); CseHttpEntity> httpEntity = new CseHttpEntity<>(body); httpEntity.addContext("contextKey", "contextValue"); String srcName = BootStrapProperties.readServiceName(environment); ResponseEntity responseEntity = template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.POST, httpEntity, Date.class); TestMgr.check(date, responseEntity.getBody()); TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2")); checkStatusCode(microserviceName, 202, responseEntity.getStatusCode()); responseEntity = template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.PATCH, httpEntity, Date.class); TestMgr.check(date, responseEntity.getBody()); TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2")); checkStatusCode(microserviceName, 202, responseEntity.getStatusCode()); int retryResult = template.getForObject(cseUrlPrefix + "retrySuccess?a=2&b=3", Integer.class); TestMgr.check(retryResult, 5); retryResult = template.getForObject(cseUrlPrefix + "retrySuccess?a=2&b=3", Integer.class); TestMgr.check(retryResult, 5); } private void testCodeFirstTestForm(RestOperations template, String cseUrlPrefix) { HttpHeaders formHeaders = new HttpHeaders(); formHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "null", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); map.put("form2", "hello"); TestMgr .check(code + "hello", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } private void testCodeFirstTestFormHighway(RestOperations template, String cseUrlPrefix) { HttpHeaders formHeaders = new HttpHeaders(); formHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", ""); HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); map = new HashMap<>(); code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", null); formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "null", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } private void testCodeFirstTestFormRest(RestOperations template, String cseUrlPrefix) { HttpHeaders formHeaders = new HttpHeaders(); formHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", ""); HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); // Rest will have empty string, but users will try to avoid depend on this, This is different from highway TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.servicecomb.demo.EmptyObject; import org.apache.servicecomb.demo.Generic; import org.apache.servicecomb.demo.compute.GenericParam; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.demo.springmvc.decoderesponse.DecodeTestResponse; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; public interface CodeFirstSpringmvcIntf { ResponseEntity responseEntity(Date date); Response cseResponse(); Response cseResponseCorrect(); Object testObject(Object input); EmptyObject testEmpty(EmptyObject input); Map testMapObject(Map input); List testListObject(List input); Holder testHolderObject(Holder input); Holder testHolderUser(Holder input); Generic testGenericUser(Generic input); Generic testGenericLong(Generic input); Generic testGenericDate(Generic input); Generic testGenericEnum(Generic input); Generic> testGenericGenericUser(Generic> input); void testvoidInRPC(); Void testVoidInRPC(); String checkQueryObject(String name, String otherName, Person requestBody); String checkQueryGenericObject(GenericParam requestBody, String str, long num); String checkQueryGenericString(String str, GenericParam requestBody, long num, String data, String strExtended, int intExtended); String testDelay(); String testAbort(); DecodeTestResponse testDecodeResponseError(); } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CustomEndpointDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.registry.discovery.AbstractEndpointDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; public class CustomEndpointDiscoveryFilter extends AbstractEndpointDiscoveryFilter { @Override protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent) { //only need rest endpoints return "rest"; } @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, StatefulDiscoveryInstance instance) { return endpoint; } @Override public int getOrder() { return 0; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ICompatible1xTestSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import io.swagger.v3.oas.annotations.Operation; public interface ICompatible1xTestSchema { String parameterName(int c, int d); @Operation(operationId = "parameterName", summary = "parameterName") String parameterNamePartMatchLeft(int a, int d); @Operation(operationId = "parameterName", summary = "parameterName") String parameterNamePartMatchRight(int c, int b); String parameterName(InvocationContext context, int c, int d); String parameterNameServerContext(int c, int d); String beanParameter(String notName, int notAge); } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; public class ResponseOKData { private String errorCode; private String errorMessage; public ResponseOKData() { } public ResponseOKData(String errorCode, String errorMessage) { this.errorCode = errorCode; this.errorMessage = errorMessage; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SchemeInterfaceSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; public interface SchemeInterfaceSpringmvc { int add(int a, int b); int reduce(int a, int b); String tailingSlash(int a, int b); String nonTailingSlash(int a, int b); } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletRequest; @RestSchema(schemaId = "SpringMVCSchema") @RequestMapping("/springMvcSchema") public class SpringMVCSchema { // TODO: examples not serialized by yaml parser @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content( schema = @Schema(implementation = String.class, example = "wget http://localhost/springMvcSchema/testApiExample"), examples = {@ExampleObject(value = "wget http://localhost/springMvcSchema/testApiExample", name = "text"), @ExampleObject(value = "{name:hello}", name = "application/json"), @ExampleObject(value = "{name:hello}", name = "json")}) , description = "success" )}) @RequestMapping(path = "/testApiExample", method = RequestMethod.POST) public String testApiExample(@RequestBody String name, HttpServletRequest request) { return null; } @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class), examples = {@ExampleObject(value = "wget http://localhost/springMvcSchema/testApiExample", name = "text"), @ExampleObject(value = "{name:hello}", name = "application/json"), @ExampleObject(value = "{name:hello}", name = "json")}) , description = "success" )}) @RequestMapping(path = "/testDefaultGetApiExample") public String testDefaultGetApiExample(@RequestParam String name, HttpServletRequest request) { return null; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.http.HttpStatus; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class TestAnnotationsSchema implements CategorizedTestCase { private static final String microserviceName = "springmvc"; private static final RestTemplate templateUrlWithServiceName = new CseRestTemplate(); @Override public void testAllTransport() throws Exception { templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); testRequiredBody(templateUrlWithServiceName, microserviceName); testRegExpPath(); } private void testRegExpPath() { String prefix = "cse://" + microserviceName; String result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a?name={name}", String.class, "a"); TestMgr.check("a", result); result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b?name={name}", String.class, "ab"); TestMgr.check("ab", result); result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b/c?name={name}", String.class, "abc"); TestMgr.check("abc", result); } private static void testRequiredBody(RestTemplate template, String microserviceName) { String prefix = "cse://" + microserviceName; Person user = new Person(); TestMgr.check("No user data found", template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", user, String.class, "ha")); user.setName("world"); TestMgr.check("ha world", template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", user, String.class, "ha")); TestMgr.check("No user data found", template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", null, String.class, "ha")); TestMgr.check("No user name found", template.postForObject(prefix + "/annotations/say", "", String.class, "ha")); TestMgr.check("test", template.postForObject(prefix + "/annotations/say", "test", String.class, "ha")); try { template.postForObject(prefix + "/annotations/testRequiredBody", null, String.class); TestMgr.fail("should fail"); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); } } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.math.BigDecimal; import java.math.BigInteger; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestBigNumberSchema implements CategorizedTestCase { interface IBigNumberSchema { BigInteger bigInteger(BigInteger intHeader, BigInteger intQuery, BigInteger intForm); BigDecimal bigDecimal(BigDecimal decimalHeader, BigDecimal decimalQuery, BigDecimal decimalForm); } @RpcReference(microserviceName = "springmvc", schemaId = "BigNumberSchema") private IBigNumberSchema schema; @Override public void testAllTransport() throws Exception { testBigInteger(); testBigDecimal(); } public void testBigInteger() { BigInteger result = schema.bigInteger(BigInteger.valueOf(100L), BigInteger.valueOf(3000000000000000000L), BigInteger.valueOf(200L)); TestMgr.check("3000000000000000300", result.toString()); } public void testBigDecimal() { BigDecimal a = BigDecimal.valueOf(100.1D); BigDecimal b = BigDecimal.valueOf(300000000000000000.1D); BigDecimal c = BigDecimal.valueOf(200.1D); BigDecimal expected = a.add(b).add(c); BigDecimal result = schema.bigDecimal(a, b, c); TestMgr.check(expected, result); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.core.MediaType; public class TestContentType { private RestOperations restTemplate = RestTemplateBuilder.create(); public void runAllTest() { testGlobalSetting(); testApiOperation(); testRequestMapping(); testProtoBuffer(); testResponseTypeOverwrite(); } private void testGlobalSetting() { HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); CseHttpEntity requestEntity = new CseHttpEntity<>("from testGlobalSetting", requestHeaders); ResponseEntity responseEntity = restTemplate .exchange("cse://springmvc/contentTypeSpringmvc/testGlobalSetting", HttpMethod.POST, requestEntity, String.class); TestMgr.check( "testGlobalSetting: name=[from testGlobalSetting], request content-type=[" + MediaType.TEXT_PLAIN + "]", responseEntity.getBody()); TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); } private void testApiOperation() { HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); CseHttpEntity requestEntity = new CseHttpEntity<>("from testApiOperation", requestHeaders); ResponseEntity responseEntity = restTemplate .exchange("cse://springmvc/contentTypeSpringmvc/testApiOperation", HttpMethod.POST, requestEntity, String.class); TestMgr.check( "testApiOperation: name=[from testApiOperation], request content-type=[" + MediaType.TEXT_PLAIN + "]", responseEntity.getBody()); TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); } private void testRequestMapping() { HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); CseHttpEntity requestEntity = new CseHttpEntity<>("from testRequestMapping", requestHeaders); ResponseEntity responseEntity = restTemplate .exchange("cse://springmvc/contentTypeSpringmvc/testRequestMapping", HttpMethod.POST, requestEntity, String.class); TestMgr.check( "testRequestMapping: name=[from testRequestMapping], request content-type=[" + MediaType.APPLICATION_JSON + "]", responseEntity.getBody()); TestMgr.check(MediaType.APPLICATION_JSON, extractContentType(responseEntity.getHeaders().getContentType())); } private void testProtoBuffer() { HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.add(HttpHeaders.CONTENT_TYPE, SwaggerConst.PROTOBUF_TYPE); User user = new User(); user.setIndex(100); user.setName("hello"); user.setNames(new String[] {"a1", "a2"}); CseHttpEntity requestEntity = new CseHttpEntity<>(user, requestHeaders); ResponseEntity responseEntity = restTemplate .exchange("cse://springmvc/contentTypeSpringmvc/testProtoBuffer", HttpMethod.POST, requestEntity, String.class); TestMgr.check( "testRequestMapping: name=[hello:100:[a1, a2]], request content-type=[application/protobuf]", responseEntity.getBody()); TestMgr.check(SwaggerConst.PROTOBUF_TYPE, extractContentType(responseEntity.getHeaders().getContentType())); } private void testResponseTypeOverwrite() { ResponseEntity responseEntity = restTemplate .getForEntity("cse://springmvc/contentTypeSpringmvcOverwrite/testResponseTypeOverwrite", String.class); TestMgr.check("testResponseTypeOverwrite: OK", responseEntity.getBody()); TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); } private String extractContentType(org.springframework.http.MediaType mediaType) { return mediaType.getType() + "/" + mediaType.getSubtype(); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.controller.Controller; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.demo.controller.PersonAlias; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.http.client.common.HttpUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestControllerImpl implements CategorizedTestCase { private static final String SERVER = "servicecomb://springmvc"; @Autowired Controller controller; RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { testQueryParamSpecial(); testResponseModel(); } @Override public void testAllTransport() throws Exception { testControllerBodyModel(); testControllerHeaderModel(); } private void testControllerHeaderModel() throws Exception { // HTTP header只允许 ASCII 字符。 // 在Header参数中使用特殊字符,需要在发送请求前进行编码,在收到响应后进行解码,具体编码方式可以业务自行选择。 // 如果不进行编码,则可能会出现乱码(HTTP协议历史上只标准化了ASCII字符,其他字符集可能工作,可能不工作)。 String encodedResult = controller.sayHei(HttpUtils.encodeURLParam("中文HTTP Header")); TestMgr.check("hei 中文HTTP Header", HttpUtils.decodeURLParam(encodedResult)); // 普通ASCII字符不需要编码 TestMgr.check("hei ~!@#$%^&*()_+-={}[]|\\:\";''<>?,./AL340", controller.sayHei("~!@#$%^&*()_+-={}[]|\\:\";''<>?,./AL340")); } private void testControllerBodyModel() { Person user = new Person(); user.setName("world"); TestMgr.check("ha world", controller.saySomething("ha", user)); } private void testResponseModel() { Person person = restTemplate.getForObject(SERVER + "/springmvc/controller/testResponseModel", Person.class); TestMgr.check("jack", person.getName()); PersonAlias personAlias = restTemplate.getForObject(SERVER + "/springmvc/controller/testResponseModel", PersonAlias.class); TestMgr.check("jack", personAlias.getName()); } private void testQueryParamSpecial() { // vert.x and servlet container have different query parameter implementations if (LegacyPropertyFactory.getBooleanProperty("servicecomb.test.vert.transport", true)) { TestMgr.check(restTemplate.getForObject( SERVER + "/springmvc/controller/sayHello1?name=you;me", String.class), "Hello you,v"); } else { TestMgr.check(restTemplate.getForObject( SERVER + "/springmvc/controller/sayHello1?name=you;me", String.class), "Hello you;me,v"); } TestMgr.check(restTemplate.getForObject( SERVER + "/springmvc/controller/sayHello1?name={1}", String.class, "you;me"), "Hello you;me,v"); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestDataTypesAnnotationsSchema implements CategorizedTestCase { public interface DataTypesAnnotationsItf { int[] testIntArrayQuery(int[] param); Integer[] testIntegerArrayQuery(Integer[] param); } @RpcReference(schemaId = "DataTypesAnnotationsSchema", microserviceName = "springmvc") private DataTypesAnnotationsItf client; @Override public void testAllTransport() throws Exception { testIntArrayQuery(); testIntegerArrayQuery(); } private void testIntArrayQuery() { int[] request = new int[] {5, 11, 4}; int[] result = client.testIntArrayQuery(request); TestMgr.check(request.length, result.length); TestMgr.check(request[1], result[1]); } private void testIntegerArrayQuery() { Integer[] request = new Integer[] {5, 11, 4}; Integer[] result = client.testIntegerArrayQuery(request); TestMgr.check(request.length, result.length); TestMgr.check(request[1], result[1]); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.net.URI; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.List; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.loadbalance.LoadBalanceFilter; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; interface DateTimeSchemaInf { Date getDate(Date date); Date getDatePath(Date date); Date postDate(Date date); LocalDate getLocalDate(LocalDate date); LocalDate getLocalDatePath(LocalDate date); LocalDate postLocalDate(LocalDate date); LocalDateTime getLocalDateTime(LocalDateTime date); LocalDateTime getLocalDateTimePath(LocalDateTime date); LocalDateTime postLocalDateTime(LocalDateTime date); } interface DateTimeSchemaWithContextInf { Date getDate(InvocationContext context, Date date); } @Component public class TestDateTimeSchema implements CategorizedTestCase { @RpcReference(microserviceName = "springmvc", schemaId = "DateTimeSchema") private DateTimeSchemaInf dateTimeSchemaInf; @RpcReference(microserviceName = "springmvc", schemaId = "DateTimeSchema") private DateTimeSchemaWithContextInf dateTimeSchemaWithContextInf; private DiscoveryTree discoveryTree; private SCBEngine scbEngine; @Autowired public void setScbEngine(SCBEngine scbEngine) { this.scbEngine = scbEngine; } @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryTree = new DiscoveryTree(discoveryManager); discoveryTree.setDiscoveryFilters(List.of(new CustomEndpointDiscoveryFilter())); } public TestDateTimeSchema() { } @Override public void testAllTransport() throws Exception { testDateTimeSchema(); testDateTimeSchemaMulticast(); testDateTimeSchemaMulticastRestTemplate(); } private void testDateTimeSchema() { Date date = new Date(); TestMgr.check(date.getTime(), dateTimeSchemaInf.getDate(date).getTime()); TestMgr.check(date.getTime(), dateTimeSchemaInf.getDatePath(date).getTime()); TestMgr.check(date.getTime(), dateTimeSchemaInf.postDate(date).getTime()); LocalDate localDate = LocalDate.of(2020, 2, 1); TestMgr.check(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), dateTimeSchemaInf.getLocalDate(localDate).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); TestMgr.check(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), dateTimeSchemaInf.getLocalDatePath(localDate).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); TestMgr.check(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), dateTimeSchemaInf.postLocalDate(localDate).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); LocalDateTime localDateTime = LocalDateTime.of(2020, 2, 1, 23, 23, 30, 333); TestMgr.check(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")), dateTimeSchemaInf.getLocalDateTime(localDateTime) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"))); TestMgr.check(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")), dateTimeSchemaInf.getLocalDateTimePath(localDateTime) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"))); TestMgr.check(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")), dateTimeSchemaInf.postLocalDateTime(localDateTime) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"))); } private void testDateTimeSchemaMulticast() throws Exception { DiscoveryContext context = new DiscoveryContext(); VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); List endpoints = serversVersionedCache.data(); for (String endpoint : endpoints) { InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); Date date = new Date(); TestMgr.check(date.getTime(), dateTimeSchemaWithContextInf.getDate(invocationContext, date).getTime()); invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, parseEndpoint(endpoint)); date = new Date(); TestMgr.check(date.getTime(), dateTimeSchemaWithContextInf.getDate(invocationContext, date).getTime()); } } private Endpoint parseEndpoint(String endpointUri) throws Exception { URI formatUri = new URI(endpointUri); Transport transport = scbEngine.getTransportManager().findTransport(formatUri.getScheme()); return new Endpoint(transport, endpointUri); } private void testDateTimeSchemaMulticastRestTemplate() throws Exception { DiscoveryContext context = new DiscoveryContext(); VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); List endpoints = serversVersionedCache.data(); RestOperations restTemplate = RestTemplateBuilder.create(); for (String endpoint : endpoints) { CseHttpEntity entity = new CseHttpEntity<>(null); InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); entity.setContext(invocationContext); Date date = new Date(); String dateValue = RestObjectMapperFactory.getRestObjectMapper().convertToString(date); TestMgr.check(date.getTime(), restTemplate .exchange("cse://springmvc/dateTime/getDate?date={1}", HttpMethod.GET, entity, Date.class, dateValue).getBody().getTime()); entity = new CseHttpEntity<>(null); invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, parseEndpoint(endpoint)); entity.setContext(invocationContext); date = new Date(); dateValue = RestObjectMapperFactory.getRestObjectMapper().convertToString(date); TestMgr.check(date.getTime(), restTemplate .exchange("cse://springmvc/dateTime/getDate?date={1}", HttpMethod.GET, entity, Date.class, dateValue).getBody().getTime()); } } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.net.URI; import java.util.Collections; import java.util.List; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class TestDownloadSchema implements CategorizedTestCase { @Autowired DiscoveryManager discoveryManager; @Override public void testRestTransport() throws Exception { testDownloadFileAndDeleted(); testDownloadFileAndDeletedCN(); testDownloadFileNotDeleted(); testDownloadFileWithNull(); testSetContentTypeByResponseEntity(); testResponseOKException(); } private void testResponseOKException() { List instances = discoveryManager.findServiceInstances("springmvctest", "springmvc"); String endpoint = instances.get(0).getEndpoints().stream() .filter(item -> item.startsWith("rest")).findFirst().get(); URI endpointItem = URI.create(endpoint); RestTemplate template = new RestTemplate(); // This is for compatible usage. For best practise, any status code // should have only one type of response. ResponseEntity resultFail = template.getForEntity( "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + "/api/download/testResponseOKExceptionBean?exception=true", ResponseOKData.class); TestMgr.check(200, resultFail.getStatusCode().value()); TestMgr.check("code-005", resultFail.getBody().getErrorCode()); TestMgr.check("error-005", resultFail.getBody().getErrorMessage()); ResponseEntity resultOK = template.getForEntity( "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + "/api/download/testResponseOKExceptionBean?exception=false", boolean.class); TestMgr.check(true, resultOK.getBody()); resultFail = template.getForEntity( "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + "/api/download/testResponseOKExceptionDownload?exception=true&content=ddd&contentType=plain/text", ResponseOKData.class); TestMgr.check(200, resultFail.getStatusCode().value()); TestMgr.check("code-005", resultFail.getBody().getErrorCode()); TestMgr.check("error-005", resultFail.getBody().getErrorMessage()); ResponseEntity resultPartOK = template.getForEntity( "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + "/api/download/testResponseOKExceptionDownload?exception=false&content=ddd&contentType=plain/text", String.class); TestMgr.check(200, resultPartOK.getStatusCode().value()); TestMgr.check("ddd", resultPartOK.getBody()); } private void testDownloadFileAndDeleted() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/deleteAfterFinished?content=hello", ReadStreamPart.class); String hello = readStreamPart.saveAsString().get(); TestMgr.check(hello, "hello"); boolean exists = restTemplate .getForObject("servicecomb://springmvc/download/assertLastFileDeleted", boolean.class); TestMgr.check(exists, false); } private void testDownloadFileAndDeletedCN() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/deleteAfterFinished?content={1}&fileName={2}", ReadStreamPart.class, "hello", "中文"); String hello = readStreamPart.saveAsString().get(); TestMgr.check(hello, "hello"); boolean exists = restTemplate .getForObject("servicecomb://springmvc/download/assertLastFileDeleted", boolean.class); TestMgr.check(exists, false); } private void testDownloadFileWithNull() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/partIsNull?content=test", ReadStreamPart.class); String result = readStreamPart.saveAsString().get(); TestMgr.check(result, "test"); readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/partIsNull?content=", ReadStreamPart.class); result = readStreamPart.saveAsString().get(); TestMgr.check(result, ""); } private void testDownloadFileNotDeleted() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate .getForObject("servicecomb://springmvc/download/notDeleteAfterFinished?content=hello", ReadStreamPart.class); String hello = readStreamPart.saveAsString().get(); TestMgr.check(hello, "hello"); boolean exists = restTemplate .getForObject("servicecomb://springmvc/download/assertLastFileDeleted", boolean.class); TestMgr.check(exists, true); } private void testSetContentTypeByResponseEntity() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); ResponseEntity responseEntity = restTemplate .getForEntity( "servicecomb://springmvc/download/setContentTypeByResponseEntity?content=hello&contentType=customType", ReadStreamPart.class); String hello = responseEntity.getBody().saveAsString().get(); TestMgr.check(responseEntity.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections.singletonList("customType")); TestMgr.check(hello, "hello"); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.springmvc.client.factory.ServiceBean; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class TestFactoryBean implements CategorizedTestCase, ApplicationContextAware { ApplicationContext applicationContext; @Override public void testRestTransport() throws Exception { ServiceBean serviceBean = (ServiceBean) this.applicationContext.getBean("ServiceFactoryBean"); TestMgr.check("a-3", serviceBean.getName()); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Date; import org.apache.servicecomb.demo.Generic; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.http.HttpStatus; import org.springframework.web.client.RestOperations; public class TestGeneric { private CodeFirstSpringmvcIntf intf; private RestOperations restTemplate = RestTemplateBuilder.create(); private String prefix = "cse://springmvc/codeFirstSpringmvc"; public TestGeneric() { intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class); } public void runRest() { testHolderUser_rest(); testGenericUser_rest(); testGenericGenericUser_rest(); testGenericLong_rest(); testGenericDate_rest(); testGenericEnum_rest(); } public void runHighway() { } public void runAllTransport() { } @SuppressWarnings("unchecked") private void testGenericEnum_rest() { Generic generic = new Generic<>(); generic.value = HttpStatus.OK; Generic result = intf.testGenericEnum(generic); TestMgr.check(HttpStatus.OK, result.value); TestMgr.check(HttpStatus.class, result.value.getClass()); result = restTemplate.postForObject(prefix + "/genericEnum", generic, Generic.class); TestMgr.check(HttpStatus.OK, result.value); TestMgr.check(HttpStatus.class, result.value.getClass()); } @SuppressWarnings({"unchecked", "deprecation"}) private void testGenericDate_rest() { Generic generic = new Generic<>(); generic.value = new Date(1001); Generic result = intf.testGenericDate(generic); TestMgr.check("1970-01-01T00:00:01.001Z", com.fasterxml.jackson.databind.util.ISO8601Utils.format(result.value, true)); TestMgr.check(Date.class, result.value.getClass()); result = restTemplate.postForObject(prefix + "/genericDate", generic, Generic.class); TestMgr.check("1970-01-01T00:00:01.001Z", com.fasterxml.jackson.databind.util.ISO8601Utils.format(result.value, true)); TestMgr.check(Date.class, result.value.getClass()); } @SuppressWarnings("unchecked") private void testGenericLong_rest() { Generic generic = new Generic<>(); generic.value = 100L; Generic result = intf.testGenericLong(generic); TestMgr.check(100, result.value); TestMgr.check(Long.class, result.value.getClass()); result = restTemplate.postForObject(prefix + "/genericLong", generic, Generic.class); TestMgr.check(100, result.value); TestMgr.check(Long.class, result.value.getClass()); } @SuppressWarnings("unchecked") private void testGenericGenericUser_rest() { Generic> generic = new Generic<>(); generic.value = new Generic<>(); generic.value.value = new User(); Generic> result = intf.testGenericGenericUser(generic); TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.value.jsonString()); result = restTemplate.postForObject(prefix + "/genericGenericUser", generic, Generic.class); TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.value.jsonString()); } @SuppressWarnings("unchecked") private void testGenericUser_rest() { Generic generic = new Generic<>(); generic.value = new User(); Generic result = intf.testGenericUser(generic); TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); result = restTemplate.postForObject(prefix + "/genericUser", generic, Generic.class); TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); } @SuppressWarnings("unchecked") private void testHolderUser_rest() { Holder holder = new Holder<>(new User()); Holder result = intf.testHolderUser(holder); TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); result = restTemplate.postForObject(prefix + "/holderUser", holder, Holder.class); TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestInvokeWhenServerNotReady.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CommonSchemaInterface; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestInvokeWhenServerNotReady { @RpcReference(schemaId = "SpringMVCCommonSchemaInterface", microserviceName = "springmvc") private CommonSchemaInterface client; // only invoke RPC before system is up, these setup is for testing calls before system ready public TestInvokeWhenServerNotReady() { startRestTemplateCall(); startRpcCall(); startInvokerUtilsCall(); } private void startRestTemplateCall() { new Thread(() -> { for (int i = 0; i < 100; i++) { try { RestOperations template = RestTemplateBuilder.create(); template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class); } catch (Throwable e) { // ignore } } }).start(); } private void startRpcCall() { new Thread(() -> { for (int i = 0; i < 100; i++) { try { InvocationContext context = new InvocationContext(); client.testInvocationTimeout(context, 1001, "customized"); } catch (Throwable e) { // ignore } } }).start(); } private void startInvokerUtilsCall() { new Thread(() -> { for (int i = 0; i < 100; i++) { try { Map args = new HashMap<>(); args.put("timeout", 1); args.put("name", "customized"); InvokerUtils .syncInvoke("springmvc", "SpringMVCCommonSchemaInterface", "testInvocationTimeout", args, String.class); } catch (Throwable e) { // ignore } } }).start(); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Map; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.solution.basic.integration.ManagementEndpoint; import org.springframework.stereotype.Component; @Component public class TestManagementEndpoint implements CategorizedTestCase { private static final String CONTENT_SCHEMA = """ openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.springmvc.server.SchemeInterfaceSpringmvc version: 1.0.0 servers: - url: /springmvc/schemaInterface paths: /add: get: operationId: add parameters: - name: a in: query required: true schema: minimum: 1 type: integer format: int32 - name: b in: query required: true schema: minimum: 1 type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: integer format: int32 /nonTailingSlash: get: operationId: nonTailingSlash parameters: - name: a in: query required: true schema: minimum: 1 type: integer format: int32 - name: b in: query required: true schema: minimum: 1 type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: string /tailingSlash/: get: operationId: tailingSlash parameters: - name: a in: query required: true schema: minimum: 1 type: integer format: int32 - name: b in: query required: true schema: minimum: 1 type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: string components: {} """; @RpcReference(microserviceName = "springmvc", schemaId = "SchemeInterfaceSpringmvc") private SchemeInterfaceSpringmvc schemeInterfaceSpringmvc; @RpcReference(microserviceName = "springmvc", schemaId = ManagementEndpoint.NAME) private ManagementEndpoint managementEndpoint; @Override public void testAllTransport() throws Exception { testSchemeInterfaceSpringmvcContentCorrect(); } @Override public void testRestTransport() throws Exception { testSchemeInterfaceSpringmvcPathSlashCorrect(); testSchemeInterfaceSpringmvcPathNonSlashCorrect(); } private void testSchemeInterfaceSpringmvcPathSlashCorrect() { String result = schemeInterfaceSpringmvc.tailingSlash(3, 5); TestMgr.check("/api/springmvc/schemaInterface/tailingSlash/;" + "/api/springmvc/schemaInterface/tailingSlash/;" + "/api/springmvc/schemaInterface/tailingSlash/;" + "-2", result); } private void testSchemeInterfaceSpringmvcPathNonSlashCorrect() { String result = schemeInterfaceSpringmvc.nonTailingSlash(3, 5); TestMgr.check("/api/springmvc/schemaInterface/nonTailingSlash;" + "/api/springmvc/schemaInterface/nonTailingSlash;" + "/api/springmvc/schemaInterface/nonTailingSlash;" + "-2", result); } private void testSchemeInterfaceSpringmvcContentCorrect() { Map contents = managementEndpoint.schemaContents(); TestMgr.check(CONTENT_SCHEMA, contents.get("SchemeInterfaceSpringmvc")); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java ================================================ /* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.apache.servicecomb.demo.springmvc.client; import static jakarta.ws.rs.core.Response.Status.REQUEST_URI_TOO_LONG; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import com.google.common.base.Strings; @Component public class TestMaxHttpUrlLength implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { if (LegacyPropertyFactory.getBooleanProperty("servicecomb.test.vert.transport", true)) { testUrlNotLongerThan4096(); } } private void testUrlNotLongerThan4096() { RestOperations restTemplate = RestTemplateBuilder.create(); // \r doesn't count for url length Since netty 4.1.88.Final See https://github.com/netty/netty/pull/12321 String q = Strings.repeat("q", 4096 + 1 - "GET /api/springmvc/controller/sayhi?name=".length() - " HTTP/1.1\r".length()); TestMgr.check("hi " + q + " [" + q + "]", restTemplate.getForObject("cse://springmvc/springmvc/controller/sayhi?name=" + q, String.class)); q = Strings.repeat("q", 4096 + 2 - "GET /api/springmvc/controller/sayhi?name=".length() - " HTTP/1.1\r".length()); try { restTemplate.getForObject("cse://springmvc/springmvc/controller/sayhi?name=" + q, String.class); TestMgr.check(true, false); } catch (InvocationException e) { TestMgr.check(REQUEST_URI_TOO_LONG.getStatusCode(), e.getStatusCode()); } } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.demo.EmptyObject; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.web.client.RestOperations; public class TestObject { private CodeFirstSpringmvcIntf intf; private RestOperations restTemplate = RestTemplateBuilder.create(); private String prefix = "cse://springmvc/codeFirstSpringmvc"; public TestObject() { intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class); } public void runRest() { } public void runHighway() { } public void runAllTransport() { testObject(); testMapObject(); testEmptyObject(); testListObject(); testHolderObject(); } @SuppressWarnings("unchecked") private void testHolderObject() { Holder holder = new Holder<>("v"); Holder result = intf.testHolderObject(holder); TestMgr.check("v", result.value); result = restTemplate.postForObject(prefix + "/holderObject", holder, Holder.class); TestMgr.check("v", result.value); } @SuppressWarnings("unchecked") private void testListObject() { List list = Collections.singletonList("v"); List result = intf.testListObject(list); TestMgr.check("[v]", result); TestMgr.check(ArrayList.class, result.getClass()); result = restTemplate.postForObject(prefix + "/listObject", list, List.class); TestMgr.check("[v]", result); TestMgr.check(ArrayList.class, result.getClass()); } @SuppressWarnings("unchecked") private void testMapObject() { Map map = Collections.singletonMap("k", "v"); Map result = intf.testMapObject(map); TestMgr.check("{k=v}", result); // This is a behavior change in 2.0.0, before 2.0.0 runtime type of RAW is HashMap TestMgr.check(LinkedHashMap.class, result.getClass()); result = restTemplate.postForObject(prefix + "/mapObject", map, Map.class); TestMgr.check("{k=v}", result); TestMgr.check(LinkedHashMap.class, result.getClass()); } private void testEmptyObject() { EmptyObject result = intf.testEmpty(new EmptyObject()); TestMgr.check(EmptyObject.class, result.getClass()); result = restTemplate.postForObject(prefix + "/emptyObject", new EmptyObject(), EmptyObject.class); TestMgr.check(EmptyObject.class, result.getClass()); } @SuppressWarnings("unchecked") private void testObject() { // int Object result = intf.testObject(1); TestMgr.check(1, result); TestMgr.check(Integer.class, result.getClass()); result = restTemplate.postForObject(prefix + "/object", 1, Integer.class); TestMgr.check(1, result); TestMgr.check(Integer.class, result.getClass()); // string result = intf.testObject("str"); TestMgr.check("str", result); TestMgr.check(String.class, result.getClass()); result = restTemplate.postForObject(prefix + "/object", "str", String.class); TestMgr.check("str", result); TestMgr.check(String.class, result.getClass()); // emptyObject result = intf.testObject(new EmptyObject()); // result may not be an empty map in highway // TestMgr.check("{}", result); TestMgr.check(true, Map.class.isAssignableFrom(result.getClass())); result = restTemplate.postForObject(prefix + "/object", new EmptyObject(), EmptyObject.class); TestMgr.check(EmptyObject.class, result.getClass()); result = restTemplate.postForObject(prefix + "/object", new EmptyObject(), EmptyObject.class); TestMgr.check(EmptyObject.class, result.getClass()); // map Map map = Collections.singletonMap("k", "v"); result = intf.testObject(map); TestMgr.check("{k=v}", result); TestMgr.check(true, Map.class.isAssignableFrom(result.getClass())); result = restTemplate.postForObject(prefix + "/object", map, Map.class); TestMgr.check("{k=v}", result); TestMgr.check(true, Map.class.isAssignableFrom(result.getClass())); // list List list = Collections.singletonList("v"); result = intf.testObject(list); TestMgr.check("[v]", result); TestMgr.check(true, List.class.isAssignableFrom(result.getClass())); result = restTemplate.postForObject(prefix + "/object", list, List.class); TestMgr.check("[v]", result); TestMgr.check(true, List.class.isAssignableFrom(result.getClass())); // generic // This test case for HIGHWAY is not stable. // When proto schema contains Holder message, the result type is Map. Or the result is Holder(using json). Holder holder = new Holder<>("v"); result = intf.testObject(holder); TestMgr.check("v", ((Map) result).get("value")); TestMgr.check(LinkedHashMap.class, result.getClass()); result = restTemplate.postForObject(prefix + "/object", holder, Holder.class); TestMgr.check("v", ((Map) result).get("value")); TestMgr.check(LinkedHashMap.class, result.getClass()); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Date; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.compute.GenericParam; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; import com.fasterxml.jackson.databind.exc.InvalidFormatException; public class TestResponse { private CodeFirstSpringmvcIntf intf; private Environment environment; public void setEnvironment(Environment environment) { this.environment = environment; } public TestResponse() { intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class); } public void runRest() { checkQueryGenericObject(); checkQueryGenericString(); testDelay(); testAbort(); testDecodeResponseError(); checkQueryObject(); testCseResponse(); testResponseEntity(); testCseResponseCorrect(); } public void runHighway() { } public void runAllTransport() { testvoidResponse(); testVoidResponse(); checkQueryObject(); testCseResponse(); testResponseEntity(); } private void testCseResponse() { String srcName = BootStrapProperties.readServiceName(environment); Response cseResponse = intf.cseResponse(); TestMgr.check("User [name=nameA, age=100, index=0]", cseResponse.getResult()); TestMgr.check("h1v " + srcName, cseResponse.getHeader("h1")); TestMgr.check("h2v " + srcName, cseResponse.getHeader("h2")); TestMgr.check(cseResponse.getStatusCode(), 202); } private void testCseResponseCorrect() { String srcName = BootStrapProperties.readServiceName(environment); Response cseResponse = intf.cseResponseCorrect(); TestMgr.check("User [name=nameA, age=100, index=0]", cseResponse.getResult()); TestMgr.check("h1v " + srcName, cseResponse.getHeader("h1")); TestMgr.check("h2v " + srcName, cseResponse.getHeader("h2")); TestMgr.check(cseResponse.getStatusCode(), 202); } private void testResponseEntity() { Date date = new Date(); String srcName = BootStrapProperties.readServiceName(environment); ResponseEntity responseEntity = intf.responseEntity(date); TestMgr.check(date, responseEntity.getBody()); TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2")); TestMgr.check(202, responseEntity.getStatusCode().value()); } private void testvoidResponse() { intf.testvoidInRPC(); } private void testVoidResponse() { intf.testVoidInRPC(); } private void checkQueryObject() { String result = intf.checkQueryObject("name1", "otherName2", new Person("bodyName")); TestMgr.check("invocationContext_is_null=false,person=name1,otherName=otherName2,name=name1,requestBody=bodyName", result); } private void checkQueryGenericObject() { GenericParam requestBody = new GenericParam<>(); requestBody.num(1).str("str1").setData(new Person("bodyPerson")); String result = intf.checkQueryGenericObject(requestBody, "str2", 2); TestMgr.check( "str=str2,generic=GenericParamWithJsonIgnore{str='str2', num=2, data=null},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", result); } private void checkQueryGenericString() { GenericParam requestBody = new GenericParam<>(); requestBody.num(1).str("str1").setData(new Person("bodyPerson")); String result = intf.checkQueryGenericString("str2", requestBody, 2, "dataTest", "strInSubclass", 33); TestMgr.check( "str=str2,generic=GenericParamExtended{strExtended='strInSubclass', intExtended=33, super=" + "GenericParam{str='str2', num=2, data=dataTest}},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", result); } private void testDelay() { StringBuilder result = new StringBuilder(); for (int i = 0; i < 4; ++i) { result.append(intf.testDelay()).append("|"); } TestMgr.check("OK|OK|OK|OK|", result.toString()); } private void testAbort() { AtomicInteger count = new AtomicInteger(0); for (int i = 0; i < 100; ++i) { try { String response = intf.testAbort(); count.incrementAndGet(); TestMgr.check("OK", response); } catch (InvocationException e) { TestMgr.check(421, e.getStatusCode()); TestMgr.check("aborted by fault inject", ((CommonExceptionData) e.getErrorData()).getMessage()); } } // 50% percent fail TestMgr.check(true, count.get() >= 30 && count.get() <= 70); } private void testDecodeResponseError() { InvocationException exception = null; try { intf.testDecodeResponseError(); } catch (InvocationException e) { // 1. InvocationException: wrapper exception exception = e; } Objects.requireNonNull(exception); // 2. CseException: bizKeeper exception Throwable cause = exception.getCause(); TestMgr.check(InvalidFormatException.class, cause.getClass()); TestMgr.check( ((InvalidFormatException) cause).getMessage().contains("Cannot deserialize value of type `java.util.Date`"), true); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRestTemplate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.compute.GenericParam; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.client.RestOperations; public class TestRestTemplate { private RestOperations restTemplate = RestTemplateBuilder.create(); public void runAllTest() { testvoidResponse(); testVoidResponse(); checkAllVoidTestResult(); checkQueryObject(); } public void runRest() { checkQueryGenericObject(); checkQueryGenericString(); checkQueryObject(); } private void testvoidResponse() { ResponseEntity resultEntity = restTemplate .getForEntity("cse://springmvc/codeFirstSpringmvc/testvoidInRestTemplate", void.class); Assert.isTrue(200 == resultEntity.getStatusCode().value(), "void return type invocation failed"); } private void testVoidResponse() { ResponseEntity resultEntity = restTemplate .getForEntity("cse://springmvc/codeFirstSpringmvc/testVoidInRestTemplate", Void.class); Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); resultEntity = restTemplate .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/testVoidInRestTemplate", Void.class); Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); resultEntity = restTemplate .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/testDefaultGetApiExample", Void.class); Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); } private void checkAllVoidTestResult() { ResponseEntity resultEntity = restTemplate .getForEntity("cse://springmvc/codeFirstSpringmvc/checkVoidResult", boolean.class); Assert.isTrue(resultEntity.getBody(), "not all void test is passed"); restTemplate .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/checkVoidResult", boolean.class); Assert.isTrue(resultEntity.getBody(), "not all void test is passed"); } private void checkQueryObject() { ResponseEntity responseEntity = restTemplate .postForEntity("cse://springmvc/codeFirstSpringmvc/checkQueryObject?name={1}&otherName={2}", new Person("bodyName"), String.class, "name1", "otherName2"); TestMgr.check("invocationContext_is_null=false,person=name1,otherName=otherName2,name=name1,requestBody=bodyName", responseEntity.getBody()); } private void checkQueryGenericObject() { GenericParam requestBody = new GenericParam<>(); requestBody.num(1).str("str1").setData(new Person("bodyPerson")); HttpEntity> requestEntity = new HttpEntity<>(requestBody); ResponseEntity responseEntity = restTemplate .exchange("cse://springmvc/codeFirstSpringmvc/checkQueryGenericObject?str={1}&num={2}", HttpMethod.PUT, requestEntity, String.class, "str2", 2); TestMgr.check( "str=str2,generic=GenericParamWithJsonIgnore{str='str2', num=2, data=null},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", responseEntity.getBody()); } private void checkQueryGenericString() { GenericParam requestBody = new GenericParam<>(); requestBody.num(1).str("str1").setData(new Person("bodyPerson")); ResponseEntity responseEntity = restTemplate.exchange( "cse://springmvc/codeFirstSpringmvc/checkQueryGenericString?str={1}&num={2}&data={3}&strExtended={4}&intExtended={5}", HttpMethod.PUT, new HttpEntity<>(requestBody), String.class, "str2", 2, "dataTest", "strInSubclass", 33); TestMgr.check( "str=str2,generic=GenericParamExtended{strExtended='strInSubclass', intExtended=33, super=" + "GenericParam{str='str2', num=2, data=dataTest}},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", responseEntity.getBody()); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRetrySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class TestRetrySchema implements CategorizedTestCase { interface RetrySchemaInf { boolean successWhenRetry(); CompletableFuture successWhenRetryAsync(); } @RpcReference(microserviceName = "springmvc", schemaId = "RetrySchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); private static final String SERVER = "servicecomb://springmvc"; @Override public void testAllTransport() throws Exception { testRetryGovernanceRestTemplate(); testRetryGovernanceRpc(); } private void testRetryGovernanceRpc() throws Exception { TestMgr.check(retrySchemaInf.successWhenRetry(), true); TestMgr.check(retrySchemaInf.successWhenRetry(), true); TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); } private void testRetryGovernanceRestTemplate() { TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSchemeInterfaceSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; @Component public class TestSchemeInterfaceSpringmvc implements CategorizedTestCase { @RpcReference(schemaId = "SchemeInterfaceSpringmvc", microserviceName = "springmvc") private SchemeInterfaceSpringmvc springmvc; public void testAllTransport() throws Exception { TestMgr.check(3, springmvc.add(1, 2)); try { springmvc.reduce(1, 3); TestMgr.failed("should throw exception", new Exception()); } catch (InvocationException e) { TestMgr.check( "Consumer method org.apache.servicecomb.demo.springmvc.client.SchemeInterfaceSpringmvc:reduce not " + "exist in contract, microserviceName=springmvc, " + "schemaId=SchemeInterfaceSpringmvc.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSpringMVCCommonSchemaInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.CommonSchemaInterface; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.stereotype.Component; @Component public class TestSpringMVCCommonSchemaInterface implements CategorizedTestCase { @RpcReference(schemaId = "SpringMVCCommonSchemaInterface", microserviceName = "springmvc") private CommonSchemaInterface client; @Override public void testAllTransport() throws Exception { testInvocationTimeoutInServer(); testInvocationTimeoutInServerUserCheck(); testInvocationAlreadyTimeoutInClient(); testInvocationTimeoutInClientWait(); } private void testInvocationTimeoutInClientWait() { try { long begin = System.currentTimeMillis(); String result = client.testInvocationTimeoutInClientWait(1000, "hello"); System.out.println("test code from testInvocationTimeoutInClientWait: " + (System.currentTimeMillis() - begin) + ":" + result); TestMgr.fail("should timeout"); } catch (InvocationException e) { TestMgr.check(408, e.getStatusCode()); TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } private void testInvocationTimeoutInServerUserCheck() { try { InvocationContext context = new InvocationContext(); client.testInvocationTimeout(context, 1001, "customized"); TestMgr.fail("should timeout"); } catch (InvocationException e) { TestMgr.check(408, e.getStatusCode()); TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } private void testInvocationAlreadyTimeoutInClient() { try { InvocationContext context = new InvocationContext(); context.addLocalContext(ProcessingTimeStrategy.CHAIN_START_TIME, System.nanoTime()); context.addLocalContext(ProcessingTimeStrategy.CHAIN_PROCESSING, TimeUnit.SECONDS.toNanos(2)); long begin = System.currentTimeMillis(); String result = client.testInvocationTimeout(context, 10, "hello"); System.out.println("testInvocationAlreadyTimeoutInClient from invocation timeout: " + (System.currentTimeMillis() - begin) + ":" + result); TestMgr.fail("should timeout"); } catch (InvocationException e) { TestMgr.check(408, e.getStatusCode()); TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } private void testInvocationTimeoutInServer() { try { client.testInvocationTimeout(1001, "hello"); TestMgr.fail("should timeout"); } catch (InvocationException e) { TestMgr.check(408, e.getStatusCode()); TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); } } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdPartyRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Date; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Component public class TestThirdPartyRegistration implements BootListener, CategorizedTestCase { @Autowired private ThirdPartyService thirdPartyService; @Override public void onAfterRegistry(BootEvent event) { } @Override public void testRestTransport() throws Exception { Date date = new Date(); ResponseEntity responseEntity = thirdPartyService.responseEntity(date); TestMgr.check(date, responseEntity.getBody()); // Third party invocation will pass cse-context to the target too TestMgr.check("h1v null", responseEntity.getHeaders().getFirst("h1")); TestMgr.check("h2v null", responseEntity.getHeaders().getFirst("h2")); TestMgr.check(202, responseEntity.getStatusCode().value()); } @Override public String getMicroserviceName() { return "testServiceName"; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Date; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.springmvc.client.ThirdSvc.ThirdSvcClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Component public class TestThirdSvc implements CategorizedTestCase { private ThirdSvcClient client; @Autowired public void setThirdSvcClient(ThirdSvcClient client) { this.client = client; } @Override public void testAllTransport() throws Exception { testResponseEntity(); testDifferentParameterName(); } private void testDifferentParameterName() { String result = client.getAuthorization("test", "param", "auth"); TestMgr.check("testparamauth", result); } private void testResponseEntity() { Date date = new Date(); ResponseEntity responseEntity = client.responseEntity(date); TestMgr.check(date, responseEntity.getBody()); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class TestTransportSchema implements CategorizedTestCase { interface TransportClient { boolean restTransport(); boolean highwayTransport(); } @RpcReference(microserviceName = "springmvc", schemaId = "TransportSchema") private TransportClient transportClient; @Override public void testRestTransport() throws Exception { testTransportSchema(); } @Override public void testHighwayTransport() throws Exception { testTransportSchema(); } @Override public void testAllTransport() throws Exception { testTransportSchema(); } private void testTransportSchema() { TestMgr.check(true, transportClient.highwayTransport()); TestMgr.check(true, transportClient.restTransport()); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; @Component public class TestUploadSchema implements CategorizedTestCase { interface FileUploadMultiInf { String fileUploadMultiRpc(List files); } @RpcReference(microserviceName = "springmvc", schemaId = "UploadSchema") private FileUploadMultiInf fileUploadMultiInf; @Override public void testRestTransport() throws Exception { testServerStartupSuccess(); testUploadMultiBigFiles(); testFileUploadMultiRpc(); testUploadFileAndAttribute(); testUploadFileAndAttributeCN(); testUploadFileRequestPartAttribute(); testUploadFileRequestPartAttributeList(); } private void testServerStartupSuccess() { RestOperations template = RestTemplateBuilder.create(); boolean result = template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class); TestMgr.check(result, true); } private void testUploadMultiBigFiles() throws Exception { final int fileNum = 5; List files = new ArrayList<>(fileNum); String fileName = UUID.randomUUID().toString(); for (int i = 0; i < fileNum; i++) { File tempFile = new File("random-client-" + fileName + i); files.add(tempFile); FileOutputStream fo = new FileOutputStream(tempFile); byte[] data = new byte[1024 * 1024 * 10]; Arrays.fill(data, (byte) 33); IOUtils.write(data, fo); fo.close(); } RestOperations template = RestTemplateBuilder.create(); MultiValueMap map = new LinkedMultiValueMap<>(); for (int i = 0; i < fileNum; i++) { map.add("files", new FileSystemResource(files.get(i))); } HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); HttpEntity> entity = new HttpEntity<>(map, headers); String result = template.postForObject("servicecomb://springmvc/upload/fileUpload", entity, String.class); TestMgr.check(result, "success"); files.forEach(File::delete); } private void testFileUploadMultiRpc() throws IOException { File file1 = File.createTempFile("file1", ".txt"); File file2 = File.createTempFile("file2", ".txt"); List files = new ArrayList<>(); files.add(new FileSystemResource(file1)); files.add(new FileSystemResource(file2)); String result = fileUploadMultiInf.fileUploadMultiRpc(files); TestMgr.check(result, "fileUploadMulti success, and fileNum is 2"); } private void testUploadFileAndAttribute() throws Exception { RestOperations template = RestTemplateBuilder.create(); Map map = new HashMap<>(); String message = "hi"; File file = File.createTempFile("file", ".txt"); FileUtils.writeStringToFile(file, "test", StandardCharsets.UTF_8, false); map.put("file", new FileSystemResource(file)); map.put("attribute", message); HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); String result = template.postForObject("servicecomb://springmvc/upload/uploadFileAndAttribute", new HttpEntity<>(map, headers), String.class); TestMgr.check("hi test", result); } private void testUploadFileAndAttributeCN() throws Exception { RestOperations template = RestTemplateBuilder.create(); Map map = new HashMap<>(); String message = "hi"; File file = File.createTempFile("中文名称", ".txt"); FileUtils.writeStringToFile(file, "test", StandardCharsets.UTF_8, false); map.put("file", new FileSystemResource(file)); map.put("attribute", message); HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); String result = template.postForObject("servicecomb://springmvc/upload/uploadFileAndAttribute", new HttpEntity<>(map, headers), String.class); TestMgr.check("hi test", result); } private void testUploadFileRequestPartAttribute() throws Exception { RestOperations template = RestTemplateBuilder.create(); Map map = new HashMap<>(); String message = "hi"; File file = File.createTempFile("file", ".txt"); FileUtils.writeStringToFile(file, "test", StandardCharsets.UTF_8, false); map.put("file", new FileSystemResource(file)); map.put("attribute", message); HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); String result = template.postForObject("servicecomb://springmvc/upload/uploadFileRequestPartAttribute", new HttpEntity<>(map, headers), String.class); TestMgr.check("hi test", result); } private void testUploadFileRequestPartAttributeList() throws Exception { RestOperations template = RestTemplateBuilder.create(); MultiValueMap map = new LinkedMultiValueMap<>(); String message1 = "msg1"; String message2 = "msg2"; File file1 = File.createTempFile("file1", ".txt"); FileUtils.writeStringToFile(file1, "test1", StandardCharsets.UTF_8, false); File file2 = File.createTempFile("file2", ".txt"); FileUtils.writeStringToFile(file2, "test2", StandardCharsets.UTF_8, false); map.add("files", new FileSystemResource(file1)); map.add("files", new FileSystemResource(file2)); map.add("attributes", message1); map.add("attributes", message2); HttpHeaders headers = new HttpHeaders(); headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); String result = template.postForObject("servicecomb://springmvc/upload/uploadFileRequestPartAttributeList", new HttpEntity<>(map, headers), String.class); TestMgr.check("test1test2msg1msg2", result); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.model.SpecialNameModel; import org.apache.servicecomb.demo.server.GenericsModel; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import com.fasterxml.jackson.core.JsonProcessingException; /* This is the provider definition: @GetMapping(path = "/diffNames") @ApiOperation(value = "differentName", nickname = "differentName") public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) and the swagger is: paths: /diffNames: get: summary: "differentName" operationId: "differentName" parameters: - name: "x" in: "query" required: true type: "integer" format: "int32" - name: "y" in: "query" required: true type: "integer" format: "int32" responses: "200": description: "response of 200" schema: type: "integer" format: "int32" In consumer, you can define any prototype that matches generated swagger of provider. */ interface DiffNames { int differentName(int x, int y); } interface DiffNames2 { int differentName(int y, int x); } interface Generics { List> genericParams(int code, List> names); } interface GenericsModelInf { GenericsModel genericParamsModel(int code, GenericsModel model); } interface SpecialNameModelInf { SpecialNameModel specialNameModel(int code, SpecialNameModel model); } @Component public class TestWeakSpringmvc implements CategorizedTestCase { @Value("${servicecomb.service.application}") private String applicationName; @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") private DiffNames diffNames; @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") private DiffNames2 diffNames2; @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") private Generics generics; @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") private GenericsModelInf genericsModelInf; @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") private SpecialNameModelInf specialNameModelInf; @RpcReference(microserviceName = "${servicecomb.service.application}:springmvc", schemaId = "weakSpringmvc") private SpecialNameModelInf specialNameModelInfWithAppId; private RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testAllTransport() throws Exception { testDiffName(); testGenerics(); testGenericsModel(); testSpecialNameModel(); } private void testSpecialNameModel() { SpecialNameModel model = new SpecialNameModel(); model.setaIntName(30); SpecialNameModel result = specialNameModelInf.specialNameModel(0, model); TestMgr.check(30, result.getaIntName()); result = specialNameModelInfWithAppId.specialNameModel(0, model); TestMgr.check(30, result.getaIntName()); } private void testGenericsModel() throws JsonProcessingException { GenericsModel model = new GenericsModel(); model.setName("model"); List> namesList = new ArrayList<>(); List names = new ArrayList<>(); names.add("hello"); namesList.add(names); model.setNameList(namesList); List>> objectLists = new ArrayList<>(); List> objectList = new ArrayList<>(); List objects = new ArrayList<>(); objects.add("object"); objectList.add(objects); objectLists.add(objectList); model.setObjectLists(objectLists); GenericsModel result = genericsModelInf.genericParamsModel(100, model); TestMgr.check(JsonUtils.writeValueAsString(model), JsonUtils.writeValueAsString(result)); } private void testGenerics() { List> namesList = new ArrayList<>(); List names = new ArrayList<>(); names.add("hello"); namesList.add(names); List> nameListResult = generics.genericParams(100, namesList); TestMgr.check(1, nameListResult.size()); TestMgr.check(1, nameListResult.get(0).size()); TestMgr.check("hello", nameListResult.get(0).get(0)); } private void testDiffName() { TestMgr.check(7, diffNames.differentName(2, 3)); TestMgr.check(8, diffNames2.differentName(2, 3)); TestMgr.check(7, restTemplate.getForObject("cse://springmvc/weakSpringmvc/diffNames?x=2&y=3", Integer.class)); TestMgr.check(7, restTemplate .getForObject("cse://" + applicationName + ":springmvc/weakSpringmvc/diffNames?x=2&y=3", Integer.class)); Map args = new HashMap<>(); args.put("x", 2); args.put("y", 3); TestMgr.check(7, InvokerUtils.syncInvoke("springmvc", "weakSpringmvc", "differentName", args, Integer.class)); TestMgr.check(7, InvokerUtils.syncInvoke(applicationName + ":springmvc", "weakSpringmvc", "differentName", args, Integer.class)); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdPartyService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.Date; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) public interface ThirdPartyService { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) ResponseEntity responseEntity(@RequestAttribute("date") Date date); } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.apache.servicecomb.provider.pojo.Invoker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @Configuration public class ThirdSvc { @Autowired Environment environment; @RequestMapping(path = "/codeFirstSpringmvc") public interface ThirdSvcClient { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) ResponseEntity responseEntity(@RequestAttribute("date") Date date); @GetMapping(value = "/getAuthorization", produces = {"application/json"}) String getAuthorization( @RequestHeader(value = "test") String test, @RequestParam(value = "param") String param, @RequestHeader("Authorization") String authorization); } @Bean public RegistryBean thirdRegistryBean() { String endpoint; if (environment.getProperty("servicecomb.test.vert.transport", boolean.class, true)) { endpoint = "rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"; } else { endpoint = "rest://localhost:8080?sslEnabled=false"; } return new RegistryBean().addSchemaInterface("schema-1", ThirdSvcClient.class) .setAppId("springmvctest") .setServiceName("3rd-svc") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(List.of(endpoint))))); } @Bean public ThirdSvcClient thirdSvcClient() { return Invoker.createProxy("3rd-svc", "schema-1", ThirdSvcClient.class); } @Bean public RegistryBean thirdPartyServiceRegistryBean() { List endpoints = new ArrayList<>(); if (environment.getProperty("servicecomb.test.vert.transport", boolean.class, true)) { endpoints.add("rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"); } else { endpoints.add("rest://localhost:8080?sslEnabled=false"); } return new RegistryBean().addSchemaInterface("testServiceName", ThirdPartyService.class) .setAppId("springmvctest") .setServiceName("testServiceName") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(endpoints)))); } @Bean public ThirdPartyService thirdPartyService() { return Invoker.createProxy("testServiceName", "testServiceName", ThirdPartyService.class); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client.factory; public class ServiceBean { private final String name; public ServiceBean(String name) { this.name = name; } public String getName() { return name; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client.factory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component("ServiceFactoryBean") public class ServiceFactoryBean implements FactoryBean { ServiceWithReference serviceWithReference; @Autowired public ServiceFactoryBean(ServiceWithReference serviceWithReference) { this.serviceWithReference = serviceWithReference; } @Override public ServiceBean getObject() throws Exception { return new ServiceBean(serviceWithReference.test("a")); } @Override public Class getObjectType() { return ServiceBean.class; } @Override public boolean isSingleton() { return true; } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client.factory; public interface ServiceWithReference { String test(String name); } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.client.factory; import org.apache.servicecomb.demo.springmvc.client.SchemeInterfaceSpringmvc; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component public class ServiceWithReferenceImpl implements ServiceWithReference { @RpcReference(schemaId = "SchemeInterfaceSpringmvc", microserviceName = "springmvc") private SchemeInterfaceSpringmvc springmvc; @Override public String test(String name) { return name + "-" + springmvc.add(1, 2); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.decoderesponse; import java.util.Date; public class DecodeTestResponse { private Date content; public Date getContent() { return content; } public void setContent(Date content) { this.content = content; } @Override public String toString() { final StringBuilder sb = new StringBuilder("DecodeTestResponse{"); sb.append("content=").append(content); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/resources/META-INF/spring/springmvc.client.bean.xml ================================================ ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- ################################################################################ ################################################################################ ################################################################################ ################################################################################ openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.springmvc.client.SpringMVCSchema version: 1.0.0 servers: - url: /springMvcSchema paths: /testApiExample: post: operationId: testApiExample requestBody: content: application/json: schema: type: string required: true x-name: name responses: "200": description: success headers: {} content: application/json: schema: type: string description: "" nullable: false example: wget http://localhost/springMvcSchema/testApiExample /testDefaultGetApiExample: get: operationId: testDefaultGetApiExample parameters: - name: name in: query required: true schema: type: string responses: "200": description: success headers: {} content: application/json: schema: type: string description: "" nullable: false example: "" components: schemas: {} ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: springmvctest name: springmvcClient version: 0.0.1 registry: sc: address: http://127.0.0.1:30100 autodiscovery: true healthCheckIntervalInSeconds: 3 pollIntervalInMillis: 90000 watch: true rest: client: connection: compression: true references: version-rule: 0+ request: clientRequestHeaderFilterEnabled: # Do not copy cse-context to target for microservice(testServiceName) testServiceName: true invocation: timeout: check: enabled: true strategy: processing-time springmvc: SpringMVCCommonSchemaInterface: testInvocationTimeoutInClientWait: timeout: 300 tracing: enabled: true samplingRate: 0.5 loadbalance: userDefinedEndpoint.enabled: true strategy: name: WeightedResponse filter.status.enabled: false fallbackpolicy: Consumer: springmvc: codeFirst: fallbackFromCache: policy: fromCache fallbackReturnNull: policy: returnNull fallbackThrowException: policy: throwException fallbackForce: policy: mycustom fallback: Consumer: springmvc: codeFirst: fallbackForce: force: true datacenter: name: myDC region: my-Region availableZone: my-Zone faultInjection: enabled: true governance: Consumer: springmvc: schemas: codeFirst: operations: testDelay: policy: fault: protocols: rest: delay: fixedDelay: 10 percent: 50 testAbort: policy: fault: protocols: rest: abort: httpStatus: 421 percent: 50 # test governance retry matchGroup: retry-governance: | matches: - apiPath: prefix: "/retry/governance/" retry-success: | matches: - apiPath: prefix: "/codeFirstSpringmvc/retrySuccess" retry: retry-governance: | maxAttempts: 2 retryOnResponseStatus: [500] retry-success: | maxAttempts: 2 retryOnResponseStatus: [503] #########SSL options # open jdk 8 now TLSv1.3 not available # ssl.protocols: TLSv1.3 # ssl.ciphers: TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384 ssl.protocols: TLSv1.2 ssl.authPeer: true ssl.checkCN.host: false #########certificates config ssl.trustStore: trust.jks ssl.trustStoreType: JKS ssl.trustStoreValue: Changeme_123 ssl.keyStore: server.p12 ssl.keyStoreType: PKCS12 ssl.keyStoreValue: Changeme_123 ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom ================================================ FILE: demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = SpringmvcClient.class) public class SpringMvcIT { @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() { SpringmvcClient.run(); Assertions.assertTrue(TestMgr.isSuccess()); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-springmvc 3.4.0-SNAPSHOT springmvc-server Java Chassis::Demo::Spring MVC::Server org.apache.servicecomb metrics-prometheus org.apache.servicecomb.demo.springmvc.SpringmvcServer org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class SpringmvcServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(SpringmvcServer.class).web(WebApplicationType.NONE).run(args); runTests(); TestMgr.summary(); if (!TestMgr.isSuccess()) { System.exit(1); } } private static void runTests() { try { CategorizedTestCaseRunner.runCategorizedTestCase("springmvc"); } catch (Exception e) { TestMgr.failed("runCategorizedTestCase failed", e); } } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.decoderesponse; public class DecodeTestResponse { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { final StringBuilder sb = new StringBuilder("DecodeTestResponse{"); sb.append("content='").append(content).append('\''); sb.append('}'); return sb.toString(); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.filter; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @Component public class ProviderTestFilter extends AbstractFilter implements ProviderFilter { @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1800; } @Override public String getName() { return "test-provider"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { invocation.addContext("k", "v"); return nextNode.onFilter(invocation); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "annotations") @RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) public class AnnotationsSchema { @GetMapping(path = "/add") public int add(@RequestParam(name = "a", defaultValue = "10") int a, @RequestParam(name = "b", defaultValue = "10") int b) { return a + b; } @RequestMapping(path = "/sayhei", method = RequestMethod.GET) public String sayHei(@RequestHeader(name = "name", defaultValue = "test") String name) { return "hei " + name; } @GetMapping(path = "/sayhi") @Parameters({ @Parameter(name = "name", in = ParameterIn.QUERY, schema = @Schema(type = "string", defaultValue = "test")), @Parameter(name = "age", in = ParameterIn.QUERY, schema = @Schema(type = "integer", defaultValue = "20")) }) public String sayHi(String name, int age) { return "hi " + name + " your age is : " + age; } @RequestMapping(path = "/saysomething", method = RequestMethod.POST) public String saySomething(String prefix, @RequestBody(required = false) Person user) { if (user == null || user.getName() == null || user.getName().isEmpty()) { return "No user data found"; } return prefix + " " + user.getName(); } @RequestMapping(path = "/say", method = RequestMethod.POST) public String say(@RequestBody(required = false) String user) { if (user == null || user.isEmpty()) { return "No user name found"; } return user; } @RequestMapping(path = "/testRequiredBody", method = RequestMethod.POST) public String testRequiredBody(@RequestBody(required = true) Person user) { if (user == null) { return "Should not happen"; } return user.getName(); } @RequestMapping(path = "/testRegExpPath/{path: .+}", method = RequestMethod.GET) public String testRegExpPath(@RequestParam("name") String name) { return name; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.math.BigDecimal; import java.math.BigInteger; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "BigNumberSchema") @RequestMapping("/bigNumber") public class BigNumberSchema { @PostMapping(path = "/integer", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public BigInteger bigInteger(@RequestHeader("intHeader") BigInteger intHeader, @RequestParam("intQuery") BigInteger intQuery, @RequestAttribute("intForm") BigInteger intForm) { return intHeader.add(intQuery).add(intForm); } @PostMapping(path = "/decimal", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public BigDecimal bigDecimal(@RequestHeader("decimalHeader") BigDecimal decimalHeader, @RequestParam("decimalQuery") BigDecimal decimalQuery, @RequestAttribute("decimalForm") BigDecimal decimalForm) { return decimalHeader.add(decimalQuery).add(decimalForm); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BizkeeperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "codeFirstBizkeeperTest") @RequestMapping(path = "/codeFirstBizkeeperTest", produces = MediaType.APPLICATION_JSON_VALUE) public class BizkeeperTest { @GetMapping(path = "/testTimeout") public String testTimeout(@RequestParam("name") String name, @RequestParam("delaytime") long delaytime) { try { Thread.sleep(delaytime); } catch (InterruptedException e) { e.printStackTrace(); } return name; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.demo.EmptyObject; import org.apache.servicecomb.demo.Generic; import org.apache.servicecomb.demo.compute.GenericParam; import org.apache.servicecomb.demo.compute.GenericParamExtended; import org.apache.servicecomb.demo.compute.GenericParamWithJsonIgnore; import org.apache.servicecomb.demo.compute.Person; import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore; import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.demo.springmvc.decoderesponse.DecodeTestResponse; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.metrics.core.MetricsBootListener; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.vertx.core.json.JsonObject; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "codeFirst") @RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) public class CodeFirstSpringmvc { private static final Logger LOGGER = LoggerFactory.getLogger(CodeFirstSpringmvc.class); private AtomicInteger invocationCounter = new AtomicInteger(0); private String _fileUpload(MultipartFile file1, Part file2) { try (InputStream is1 = file1.getInputStream(); InputStream is2 = file2.getInputStream()) { String content1 = IOUtils.toString(is1, StandardCharsets.UTF_8); String content2 = IOUtils.toString(is2, StandardCharsets.UTF_8); return String.format("%s:%s:%s\n" + "%s:%s:%s", file1.getOriginalFilename(), file1.getContentType(), content1, file2.getSubmittedFileName(), file2.getContentType(), content2); } catch (IOException e) { throw new IllegalArgumentException(e); } } @GetMapping(path = "/retrySuccess") public int retrySuccess(@RequestParam("a") int a, @RequestParam("b") int b) { if (invocationCounter.getAndIncrement() % 3 != 0) { throw new InvocationException(Status.SERVICE_UNAVAILABLE, "try again later."); } return a + b; } @PostMapping(path = "/upload1", produces = MediaType.TEXT_PLAIN_VALUE) public String fileUpload1(@RequestPart(name = "file1") MultipartFile file1) throws IOException { try (InputStream is = file1.getInputStream()) { return IOUtils.toString(is, StandardCharsets.UTF_8); } } @PostMapping(path = "/upload", produces = MediaType.TEXT_PLAIN_VALUE) public String fileUpload(@RequestPart(name = "file1") MultipartFile file1, @RequestPart(name = "someFile") Part file2) { return _fileUpload(file1, file2); } @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) public ResponseEntity responseEntity(InvocationContext c1, @RequestAttribute("date") Date date) { HttpHeaders headers = new HttpHeaders(); headers.add("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); headers.add("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return new ResponseEntity<>(date, headers, HttpStatus.ACCEPTED); } @GetMapping(value = "/getAuthorization", produces = {"application/json"}) public String getAuthorization( @RequestHeader(value = "test") String test, @RequestParam(value = "param") String param, @Parameter(description = "Authorization header", required = true, in = ParameterIn.HEADER, name = "Authorization") @RequestHeader("Authorization") String authorization) { return test + param + authorization; } @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.PATCH) public ResponseEntity responseEntityPATCH(InvocationContext c1, @RequestAttribute("date") Date date) { return responseEntity(c1, date); } // This definition is not correct, response type is not // same as the actual one. May be not support in future. @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = User.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/cseResponse", method = RequestMethod.GET) public Response cseResponse(InvocationContext c1) { Response response = Response.createSuccess(Status.ACCEPTED, new User()); response.addHeader("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); response.addHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return response; } // This definition is correct, but not supported by highway. // highway do not support define code other than 200 @ApiResponse(responseCode = "202", content = @Content(schema = @Schema(implementation = User.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/cseResponseCorrect", method = RequestMethod.GET) public Response cseResponseCorrect(InvocationContext c1) { Response response = Response.createSuccess(Status.ACCEPTED, new User()); response.addHeader("h1", "h1v " + c1.getContext().get(CoreConst.SRC_MICROSERVICE)); InvocationContext c2 = ContextUtils.getInvocationContext(); response.addHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); return response; } @PostMapping(path = "/testUserMap") public Map testUserMap(@RequestBody Map userMap) { return userMap; } @RequestMapping(path = "/textPlain", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) public String textPlain(@RequestBody String body) { return body; } @RequestMapping(path = "/bytes", method = RequestMethod.POST) public byte[] bytes(@RequestBody byte[] input) { input[0] = (byte) (input[0] + 1); return input; } @RequestMapping(path = "/addDate", method = RequestMethod.POST) public Date addDate(@RequestAttribute("date") Date date, @QueryParam("seconds") long seconds) { return new Date(date.getTime() + seconds * 1000); } // this should be ignored as it's hidden @Operation(summary = "", hidden = true, method = "POST") public int add(@RequestParam("a") int a) { return a; } @RequestMapping(path = "/add", method = RequestMethod.POST) public int add(@RequestAttribute("a") int a, @RequestAttribute("b") int b) { return a + b; } @GetMapping(path = "/reduce") @Parameters({@Parameter(name = "a", schema = @Schema(implementation = int.class), in = ParameterIn.QUERY)}) public int reduce(HttpServletRequest request, @CookieValue(name = "b") int b) { int a = Integer.parseInt(request.getParameter("a")); return a - b; } @RequestMapping(path = "/sayhello", method = RequestMethod.POST) public Person sayHello(@RequestBody Person user) { user.setName("hello " + user.getName()); return user; } @SuppressWarnings("unchecked") @RequestMapping(path = "/testrawjson", method = RequestMethod.POST) public String testRawJsonString(String jsonInput) { Map person; try { person = RestObjectMapperFactory.getRestObjectMapper() .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; } return "hello " + person.get("name"); } @RequestMapping(path = "/saysomething", method = RequestMethod.POST) public String saySomething(@RequestHeader(name = "prefix") String prefix, @RequestBody Person user) { return prefix + " " + user.getName(); } @PutMapping(path = "/sayhi/{name}") public String sayHi(@PathVariable(name = "name") String name) { ContextUtils.getInvocationContext().setStatus(202); return name + " sayhi"; } @RequestMapping(path = "/sayhi/compressed/{name}/v2", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE) public String sayHiForCompressed(@PathVariable(name = "name") String name) { String bigText = "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text!"; return name + " sayhi compressed:" + bigText; } @RequestMapping(path = "/sayhi/{name}/v2", method = RequestMethod.PUT) public String sayHi2(@PathVariable(name = "name") String name) { return name + " sayhi 2"; } @RequestMapping(path = "/istrue", method = RequestMethod.GET) public boolean isTrue() { return true; } @DeleteMapping(path = "/addstring", produces = MediaType.TEXT_PLAIN_VALUE) public String addString(@RequestParam(name = "s") List s) { StringBuilder result = new StringBuilder(); for (String x : s) { result.append(x); } return result.toString(); } // Using 490, 590 error code, the response type should be CommonExceptionData. Or we need // complex ExceptionConverters to deal with exceptions thrown by Handlers, etc. @RequestMapping(path = "/fallback/returnnull/{name}", method = RequestMethod.GET) @ApiResponses(value = {@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = "xxx"), @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)), description = "xxx")}) public String fallbackReturnNull(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); } return name; } @RequestMapping(path = "/fallback/throwexception/{name}", method = RequestMethod.GET) @ApiResponses(value = { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) , description = "xxx"), @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) , description = "xxx")}) public String fallbackThrowException(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); } return name; } @RequestMapping(path = "/fallback/fromcache/{name}", method = RequestMethod.GET) @ApiResponses(value = { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) , description = "xxx"), @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) , description = "xxx")}) public String fallbackFromCache(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); } return name; } @RequestMapping(path = "/fallback/force/{name}", method = RequestMethod.GET) @ApiResponses(value = { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) , description = "xxx"), @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) , description = "xxx")}) public String fallbackForce(@PathVariable(name = "name") String name) { if ("throwexception".equals(name)) { throw new InvocationException(490, "490", new CommonExceptionData("xxx")); } return name; } public enum NameType { abc, def } @RequestMapping(path = "/testenum/{name}", method = RequestMethod.GET) @ApiResponses(value = { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)) , description = "xxx"), @ApiResponse(responseCode = "490", content = @Content(schema = @Schema(implementation = CommonExceptionData.class)) , description = "xxx")}) public String testEnum(@RequestParam(name = "username") String username, @PathVariable(value = "name") NameType nameType) { return nameType.toString(); } @RequestMapping(method = RequestMethod.POST, value = "/ignore") @ResponseBody public OutputModelForTestIgnore testModelWithIgnoreField(@RequestBody InputModelForTestIgnore input) { return new OutputModelForTestIgnore("output_id", input.getInputId(), input.getContent(), input.getInputObject(), input.getInputJsonObject(), input.getInputIgnoreInterface(), new Person("outputSomeone"), new JsonObject("{\"OutputJsonKey\" : \"OutputJsonValue\"}"), () -> { }); } @SuppressWarnings("unchecked") @RequestMapping(method = RequestMethod.POST, value = "/rawJsonAnnotation") @ResponseBody public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { Map person; try { person = RestObjectMapperFactory.getRestObjectMapper() .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); } catch (Exception e) { e.printStackTrace(); return null; } return "hello " + person.get("name"); } @PostMapping(path = "/testform") @io.swagger.v3.oas.annotations.parameters.RequestBody( content = {@Content(mediaType = SwaggerConst.FORM_MEDIA_TYPE, schema = @Schema(name = "form1", implementation = String.class, nullable = false, description = "a required form param")), @Content(mediaType = SwaggerConst.FORM_MEDIA_TYPE, schema = @Schema(name = "form2", implementation = String.class, nullable = true, description = "an optional form param"))}) public String testform(HttpServletRequest request) { String form1 = request.getParameter("form1"); String form2 = request.getParameter("form2"); Assert.notNull(form1, "from1 is null"); return form1 + form2; } @Autowired MetricsBootListener metricsBootListener; //Only for Prometheus integration test @RequestMapping(path = "/prometheusForTest", method = RequestMethod.GET) public String prometheusForTest() { // just for test, this makes client always can got latest metrics metricsBootListener.getMetricsBootstrap().pollMeters(); RestTemplate defaultRestTemplate = new RestTemplate(); return defaultRestTemplate.getForObject("http://localhost:9696/metrics", String.class); } @GetMapping(path = "/traceId") public String getTraceId() { return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); } @PostMapping(path = "/emptyObject") public EmptyObject testEmpty(@RequestBody EmptyObject input) { return input; } @PostMapping(path = "/object") public Object testObject(@RequestBody Object input) { return input; } @PostMapping(path = "/mapObject") public Map testMapObject(@RequestBody Map input) { return input; } @PostMapping(path = "/listObject") public List testListObject(@RequestBody List input) { return input; } @PostMapping(path = "/holderObject") public Holder testHolderObject(@RequestBody Holder input) { return input; } @PostMapping(path = "/holderUser") public Holder testHolderUser(@RequestBody Holder input) { Assert.isInstanceOf(Holder.class, input); Assert.isInstanceOf(User.class, input.value); return input; } @PostMapping(path = "/genericUser") public Generic testGenericUser(@RequestBody Generic input) { Assert.isInstanceOf(Generic.class, input); Assert.isInstanceOf(User.class, input.value); return input; } @PostMapping(path = "/genericLong") public Generic testGenericLong(@RequestBody Generic input) { Assert.isInstanceOf(Generic.class, input); Assert.isInstanceOf(Long.class, input.value); return input; } @PostMapping(path = "/genericDate") public Generic testGenericDate(@RequestBody Generic input) { Assert.isInstanceOf(Generic.class, input); Assert.isInstanceOf(Date.class, input.value); System.out.println(input.value); return input; } @PostMapping(path = "/genericEnum") public Generic testGenericEnum(@RequestBody Generic input) { Assert.isInstanceOf(Generic.class, input); Assert.isInstanceOf(HttpStatus.class, input.value); return input; } @PostMapping(path = "/genericGenericUser") public Generic> testGenericGenericUser(@RequestBody Generic> input) { Assert.isInstanceOf(Generic.class, input); Assert.isInstanceOf(Generic.class, input.value); Assert.isInstanceOf(User.class, input.value.value); return input; } private volatile boolean testvoidInRPCSuccess = false; @GetMapping(path = "/testvoidInRPC") public void testvoidInRPC() { LOGGER.info("testvoidInRPC() is called!"); testvoidInRPCSuccess = true; } private volatile boolean testVoidInRPCSuccess = false; @GetMapping(path = "/testVoidInRPC") public Void testVoidInRPC() { LOGGER.info("testVoidInRPC() is called!"); testVoidInRPCSuccess = true; return null; } private volatile boolean testvoidInRestTemplateSuccess = false; @GetMapping(path = "/testvoidInRestTemplate") public void testvoidInRestTemplate() { LOGGER.info("testvoidInRestTemplate() is called!"); testvoidInRestTemplateSuccess = true; } private volatile boolean testVoidInRestTemplateSuccess = false; @GetMapping(path = "/testVoidInRestTemplate") public Void testVoidInRestTemplate() { LOGGER.info("testVoidInRestTemplate() is called!"); testVoidInRestTemplateSuccess = true; return null; } @GetMapping(path = "/checkVoidResult") public boolean checkVoidResult() { LOGGER.info("checkVoidResult() is called!"); return testvoidInRPCSuccess && testVoidInRPCSuccess && testvoidInRestTemplateSuccess && testVoidInRestTemplateSuccess; } /** * Simple query object test, users can use it mixed with InvocationContext and plain query param, RequestBody */ @PostMapping(path = "/checkQueryObject") public String checkQueryObject(Person person, @RequestParam(name = "otherName") String otherName, InvocationContext invocationContext, @RequestBody Person requestBody) { LOGGER.info("checkQueryObject() is called!"); return "invocationContext_is_null=" + (null == invocationContext) + ",person=" + person + ",otherName=" + otherName + ",name=" + person.getName() + ",requestBody=" + requestBody; } /** * For the nesting object params, including the generic params whose generic field is an object, * the inner object field is not supported. */ @PutMapping(path = "/checkQueryGenericObject") public String checkQueryGenericObject(@RequestBody GenericParam requestBody, GenericParamWithJsonIgnore generic) { LOGGER.info("checkQueryGenericObject() is called!"); return "str=" + generic.getStr() + ",generic=" + generic + ",requestBody=" + requestBody; } /** * If the generic field is simple type, it's supported to be deserialized. * The same for those simple type field inherited from the parent class. */ @PutMapping(path = "/checkQueryGenericString") public String checkQueryGenericString(@RequestBody GenericParam requestBody, GenericParamExtended generic) { LOGGER.info("checkQueryGenericObject() is called!"); return "str=" + generic.getStr() + ",generic=" + generic + ",requestBody=" + requestBody; } @GetMapping(path = "/testDelay") public String testDelay() { LOGGER.info("testDelay() is called!"); return "OK"; } @GetMapping(path = "/testAbort") public String testAbort() { LOGGER.info("testAbort() is called!"); return "OK"; } @GetMapping(path = "/testDecodeResponseError") public DecodeTestResponse testDecodeResponseError() { DecodeTestResponse response = new DecodeTestResponse(); response.setContent("returnOK"); return response; } @RequestMapping(path = "/testDefaultGetApiExample") public void testDefaultGetApiExample() { LOGGER.info("testDefaultGetApiExample() is called!"); } @RequestMapping(path = "/testHolder") public void testHolder(@RequestBody Holder name) { LOGGER.info("testDefaultGetApiExample() is called!"); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvcForSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; /** * Created for testing schema to accommodate swagger api specification. When change this class, please use * http://editor.swagger.io/ to validate the generated schema and should not give any errors. In test case, * only checksum is validated to make sure schema is not changed. */ @RestSchema(schemaId = "CodeFirstSpringmvcForSchema") @RequestMapping(path = "/forSchema") public class CodeFirstSpringmvcForSchema { /* * Using http://editor.swagger.io/ . Listing errors not handled: * * #1. Should NOT have additional properties additionalProperty: type, format, name, in, required /reduce: get: operationId: "reduce" parameters: - name: "b" in: "cookie" required: false This schema gives error, but according to https://swagger.io/docs/specification/describing-parameters/#cookie-parameters This should supported. * #2. Operations with Parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property This error can be fixed by user code. * */ @RequestMapping(path = "/uploadFile", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public boolean uploadAwardFile(@RequestParam("fileType") String fileType, @RequestParam("zoneId") String zoneId, @RequestPart("file") MultipartFile file) { throw new UnsupportedOperationException("only for testing schema"); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/Compatible1xTestSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "Compatible1xTestSchema") @RequestMapping(path = "/compatible", produces = MediaType.APPLICATION_JSON) public class Compatible1xTestSchema { @GetMapping(path = "/parameterName") public String parameterName(@RequestParam(name = "a", defaultValue = "10") int a, @RequestParam(name = "b", defaultValue = "10") int b) { return ContextUtils.getInvocationContext().getContext(CoreConst.SRC_MICROSERVICE) + a + b * 2; } @GetMapping(path = "/parameterNameServerContext") public String parameterNameServerContext(InvocationContext context, @RequestParam(name = "a", defaultValue = "10") int a, @RequestParam(name = "b", defaultValue = "10") int b) { return context.getContext(CoreConst.SRC_MICROSERVICE) + a + b * 2; } @GetMapping(path = "/beanParameter") public String beanParameter(CompatibleQueryBean bean) { return bean.getName() + bean.getAge(); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CompatibleQueryBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; public class CompatibleQueryBean { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ConfigurationProblemsCollectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.core.bootup.ConfigurationProblemsAlarmEvent; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.stereotype.Component; import com.google.common.eventbus.Subscribe; @Component public class ConfigurationProblemsCollectorTest implements CategorizedTestCase { private ConfigurationProblemsAlarmEvent event; public ConfigurationProblemsCollectorTest() { EventManager.register(this); } @Subscribe public void onConfigurationProblemsAlarmEvent(ConfigurationProblemsAlarmEvent event) { this.event = event; } @Override public void testRestTransport() throws Exception { TestMgr.check(event != null, true); TestMgr.check(event.getProblems(), "[WARN]Configurations warnings:\n" + "Configurations with prefix `service_description` is deprecated, " + "use `servicecomb.service` instead. Find keys [service_description.initialStatus]\n" + "Configuration `servicecomb.loadbalance.isolation.*` is removed, use governance instead. " + "See https://servicecomb.apache.org/references/java-chassis/zh_CN/references-handlers/governance-best-practise.html"); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.util.Arrays; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "contentTypeSpringmvc") @RequestMapping(value = "/contentTypeSpringmvc", consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_PLAIN) public class ContentTypeSpringmvc { @RequestMapping(path = "/testGlobalSetting", method = RequestMethod.POST) public String testGlobalSetting(@RequestBody String name, HttpServletRequest request) { return String.format("testGlobalSetting: name=[%s], request content-type=[%s]", name, request.getContentType()); } @RequestMapping(path = "/testApiOperation", method = RequestMethod.POST) public String testApiOperation(@RequestBody String name, HttpServletRequest request) { return String.format("testApiOperation: name=[%s], request content-type=[%s]", name, request.getContentType()); } @RequestMapping(path = "/testRequestMapping", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON) public String testRequestMapping(@RequestBody String name, HttpServletRequest request) { return String.format("testRequestMapping: name=[%s], request content-type=[%s]", name, request.getContentType()); } @RequestMapping(path = "/testProtoBuffer", method = RequestMethod.POST, consumes = SwaggerConst.PROTOBUF_TYPE, produces = SwaggerConst.PROTOBUF_TYPE) public String testProtoBuffer(@RequestBody User user, HttpServletRequest request) { return String.format("testRequestMapping: name=[%s], request content-type=[%s]", user.getName() + ":" + user.getIndex() + ":" + Arrays.toString(user.getNames()), request.getContentType()); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "contentTypeSpringmvcOverwrite") @RequestMapping(value = "/contentTypeSpringmvcOverwrite", produces = MediaType.TEXT_PLAIN) public class ContentTypeSpringmvcOverwrite { @RequestMapping(value = "/testResponseTypeOverwrite", method = RequestMethod.GET) public String testResponseTypeOverwrite() { return "testResponseTypeOverwrite: OK"; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ControllerImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.util.Arrays; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Min; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; // This class tests "contract first", the controller.yaml will override annotations defined in class. @RestSchema(schemaId = "controller") @RequestMapping(path = "/springmvc/controller", produces = MediaType.APPLICATION_JSON) public class ControllerImpl { @GetMapping(path = "/add") public int add(@Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b) { return a + b; } @PostMapping(path = "/sayhello/{name}") public String sayHello(@PathVariable("name") String name) { if ("exception".equals(name)) { throw new InvocationException(Status.SERVICE_UNAVAILABLE, ""); } return "hello " + name; } @RequestMapping(path = "/saysomething", method = RequestMethod.POST) public String saySomething(String prefix, @RequestBody Person user) { return prefix + " " + user.getName(); } @Transport(name = CoreConst.RESTFUL) @RequestMapping(path = "/sayhi", method = RequestMethod.GET) public String sayHi(HttpServletRequest request) throws Exception { String addr = request.getRemoteAddr(); if (addr == null || addr.isEmpty()) { throw new Exception("Can't get remote addr!"); } String[] values = request.getParameterValues("name"); if (values != null && values.length > 0 && values[0].equals("throwexception")) { throw new RuntimeException(); } return "hi " + request.getParameter("name") + " " + Arrays.toString(values); } @RequestMapping(path = "/sayhei", method = RequestMethod.GET) public String sayHei(@RequestHeader("name") String name) { return "hei " + name; } @RequestMapping(path = "/sayHello1", method = RequestMethod.GET) public String sayHello1(@RequestParam("name") String name) { return "Hello " + name + "," + ContextUtils.getInvocationContext().getContext("k"); } @RequestMapping(path = "/testResponseModel", method = RequestMethod.GET) public Person testResponseModel() { Person person = new Person(); person.setName("jack"); return person; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DataTypesAnnotationsSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "DataTypesAnnotationsSchema") @RequestMapping(path = "/dataTypes") public class DataTypesAnnotationsSchema { @GetMapping(path = "/testIntArrayQuery") public int[] testIntArrayQuery(@RequestParam("param") int[] param) { return param; } @GetMapping(path = "/testIntegerArrayQuery") public Integer[] testIntegerArrayQuery(@RequestParam("param") Integer[] param) { return param; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DateTimeSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "DateTimeSchema") @RequestMapping(path = "/dateTime", produces = MediaType.APPLICATION_JSON) public class DateTimeSchema { @GetMapping(path = "/getDate") public Date getDate(@RequestParam("date") Date date) { return date; } @GetMapping(path = "/getDatePath/{date}") public Date getDatePath(@PathParam("date") Date date) { return date; } @PostMapping(path = "/postDate") public Date postDate(@RequestBody Date date) { return date; } @GetMapping(path = "/getLocalDate") public LocalDate getLocalDate(@RequestParam("date") LocalDate date) { return date; } @GetMapping(path = "/getLocalDate/{date}") public LocalDate getLocalDatePath(@PathParam("date") LocalDate date) { return date; } @PostMapping(path = "/postLocalDate") public LocalDate postLocalDate(@RequestBody LocalDate date) { return date; } @GetMapping(path = "/getLocalDateTime") public LocalDateTime getLocalDateTime(@RequestParam("date") LocalDateTime date) { return date; } @GetMapping(path = "/getLocalDateTime/{date}") public LocalDateTime getLocalDateTimePath(@PathParam("date") LocalDateTime date) { return date; } @PostMapping(path = "/postLocalDateTime") public LocalDateTime postLocalDateTime(@RequestBody LocalDateTime date) { return date; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.servlet.http.Part; @RestSchema(schemaId = "DownloadSchema") @RequestMapping(path = "/download", produces = MediaType.APPLICATION_JSON_VALUE) public class DownloadSchema { private File tempDir = new File("target/downloadTemp"); private File lastFile; private File createTempFile(String content) throws IOException { return createTempFile(null, content); } private File createTempFile(String name, String content) throws IOException { if (name == null) { name = "download-" + UUID.randomUUID() + ".txt"; } File file = new File(tempDir, name); FileUtils.write(file, content, StandardCharsets.UTF_8, false); lastFile = file; return file; } @GetMapping(path = "/deleteAfterFinished") public ResponseEntity deleteAfterFinished(@RequestParam("content") String content, @RequestParam(value = "fileName", required = false) String fileName) throws IOException { File file; if (StringUtils.isNotEmpty(fileName)) { file = createTempFile(fileName, content); } else { file = createTempFile(content); } return ResponseEntity .ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") .body(new FilePart(null, file) .setDeleteAfterFinished(true)); } @GetMapping(path = "/partIsNull") public ResponseEntity partIsNull(@RequestParam("content") String content) throws IOException { if (StringUtils.isEmpty(content)) { return ResponseEntity .ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") .body(null); } File file = createTempFile(content); return ResponseEntity .ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") .body(new FilePart(null, file)); } @GetMapping(path = "/notDeleteAfterFinished") public ResponseEntity notDeleteAfterFinished(@RequestParam("content") String content) throws IOException { File file = createTempFile(content); return ResponseEntity .ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") .body(new FilePart(null, file)); } @GetMapping(path = "/setContentTypeByResponseEntity") public ResponseEntity setContentTypeByResponseEntity(@RequestParam("content") String content, @RequestParam("contentType") String contentType) throws IOException { File file = createTempFile(content); return ResponseEntity .ok() .header(HttpHeaders.CONTENT_TYPE, contentType) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") .body(new FilePart(null, file)); } @GetMapping(path = "/assertLastFileDeleted") public boolean assertLastFileDeleted() { return lastFile.exists(); } @GetMapping(path = "/testResponseOKExceptionDownload") public ResponseEntity testResponseOKExceptionDownload( @RequestParam("exception") boolean exception, @RequestParam("content") String content, @RequestParam("contentType") String contentType) throws IOException { if (exception) { throw new ResponseOKException(); } File file = createTempFile(content); return ResponseEntity .ok() .header(HttpHeaders.CONTENT_TYPE, contentType) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") .body(new FilePart(null, file)); } @GetMapping(path = "/testResponseOKExceptionBean") public boolean testResponseOKExceptionBean(@RequestParam("exception") boolean exception) { if (exception) { throw new ResponseOKException(); } return true; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; public class MyStrategy extends AbstractQpsStrategy { @Override public boolean isLimitNewRequest() { return false; } @Override public String name() { return "MyStrategy"; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; import org.apache.servicecomb.qps.strategy.IStrategyFactory; public class MyStrategyFactory implements IStrategyFactory { @Override public AbstractQpsStrategy createStrategy(String strategyName) { if (strategyName.equals("MyStrategy")) { return new MyStrategy(); } return null; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectWriter; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; /** * Testing after bootup. */ @Component public class ProducerTestsAfterBootup implements BootListener { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerTestsAfterBootup.class); private ObjectWriter writer = Yaml.pretty(); public void testSchemaNotChange(SCBEngine scbEngine) { LOGGER.info("ProducerTestsAfterBootup testing start"); SchemaMeta meta = scbEngine.getProducerMicroserviceMeta().findSchemaMeta("CodeFirstSpringmvcForSchema"); String codeFirst = getSwaggerContent(meta.getSwagger()); String expectSchema = UnitTestSwaggerUtils.loadExpect("schemas/CodeFirstSpringmvcForSchema.yaml") .replace("\r\n", "\n").trim(); int offset = expectSchema.indexOf("---\nopenapi: 3.0.1"); if (offset > 0) { expectSchema = expectSchema.substring(offset + 4); } TestMgr.check(expectSchema.trim(), codeFirst.trim()); } private String getSwaggerContent(OpenAPI swagger) { try { return writer.writeValueAsString(swagger); } catch (JsonProcessingException e) { throw new Error(e); } } @Override public void onBootEvent(BootEvent event) { if (event.getEventType() == BootListener.EventType.AFTER_REGISTRY) { testSchemaNotChange(event.getScbEngine()); if (!TestMgr.isSuccess()) { TestMgr.summary(); throw new IllegalStateException("some tests are failed. "); } } } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ReadFileSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "testFileSource") @RequestMapping(path = "/springmvc/fileSource") public class ReadFileSource { @Value("${int:-1}") public int testInt; @GetMapping(path = "/int") public int getTestInt() { return testInt; } @Value("${String:error}") public String testString; @GetMapping(path = "/String") public String getTestString() { return testString; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; public class ResponseOKData { private String errorCode; private String errorMessage; public ResponseOKData(String errorCode, String errorMessage) { this.errorCode = errorCode; this.errorMessage = errorMessage; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; public class ResponseOKException extends RuntimeException { } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; public class ResponseOKExceptionConverter implements ExceptionConverter { @Override public boolean canConvert(Throwable throwable) { return throwable instanceof ResponseOKException; } @Override public InvocationException convert(Invocation invocation, ResponseOKException throwable, StatusType genericStatus) { // This is for compatible usage. For best practise, any status code // should have only one type of response. return new InvocationException(Status.OK, new ResponseOKData("code-005", "error-005")); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestProducers; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @Component @Primary // This example shows user's how to customize RestProducers by @Primary annotation public class RestProducersCustomized extends RestProducers { } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; // test cases for retry @RestSchema(schemaId = "RetrySchema") @RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) public class RetrySchema { private AtomicLong counter = new AtomicLong(0); @GetMapping(path = "/governance/successWhenRetry") public boolean successWhenRetry() { if (counter.getAndIncrement() % 3 != 0) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); } return true; } @GetMapping(path = "/governance/successWhenRetryAsync") public CompletableFuture successWhenRetryAsync() { CompletableFuture result = new CompletableFuture<>(); if (counter.getAndIncrement() % 2 == 0) { result.completeExceptionally(new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later.")); } else { result.complete(true); } return result; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Min; import jakarta.ws.rs.core.MediaType; @RequestMapping(path = "/springmvc/schemaInterface", produces = MediaType.APPLICATION_JSON) public interface SchemeInterfaceSpringmvc { @GetMapping(path = "/add") int add(@Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); @GetMapping(path = "/tailingSlash/") String tailingSlash(HttpServletRequest request, @Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); @GetMapping(path = "/nonTailingSlash") String nonTailingSlash(HttpServletRequest request, @Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvcImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.provider.rest.common.RestSchema; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Min; @RestSchema(schemaId = "SchemeInterfaceSpringmvc", schemaInterface = SchemeInterfaceSpringmvc.class) public class SchemeInterfaceSpringmvcImpl implements SchemeInterfaceSpringmvc { @Override public int add(@Min(1) int a, @Min(1) int b) { return a + b; } @Override public String tailingSlash(HttpServletRequest request, int a, int b) { StringBuilder sb = new StringBuilder(); sb.append(request.getRequestURI()).append(";") .append(request.getRequestURL()).append(";") .append(request.getPathInfo()).append(";") .append(a - b); return sb.toString(); } @Override public String nonTailingSlash(HttpServletRequest request, int a, int b) { StringBuilder sb = new StringBuilder(); sb.append(request.getRequestURI()).append(";") .append(request.getRequestURL()).append(";") .append(request.getPathInfo()).append(";") .append(a - b); return sb.toString(); } public int reduce(int a, int b) { return a - b; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMVCCommonSchemaInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.demo.CommonSchemaInterface; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @RestSchema(schemaId = "SpringMVCCommonSchemaInterface", schemaInterface = CommonSchemaInterface.class) public class SpringMVCCommonSchemaInterface implements CommonSchemaInterface { @Override public String testInvocationTimeout(long timeout, String name) { try { Thread.sleep(timeout); } catch (InterruptedException e) { } return name; } @Override public String testInvocationTimeout(InvocationContext context, long timeout, String name) { if ("customized".equals(name)) { try { Thread.sleep(timeout); } catch (InterruptedException e) { } Invocation invocation = (Invocation) context; invocation.ensureInvocationNotTimeout(); throw new InvocationException(Status.BAD_REQUEST, "not expected result"); } return testInvocationTimeout(timeout, name); } @Override public String testInvocationTimeoutInClientWait(long timeout, String name) { return testInvocationTimeout(timeout, name); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMvcDefaultValues.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "SpringMvcDefaultValues") @RequestMapping(path = "/springmvc/SpringMvcDefaultValues", produces = MediaType.APPLICATION_JSON) public class SpringMvcDefaultValues { @PostMapping(path = "/form", consumes = MediaType.APPLICATION_FORM_URLENCODED) public String form(@RequestParam(name = "a", defaultValue = "20") int a, @RequestParam(name = "b", defaultValue = "bobo") String b) { return "Hello " + a + b; } @PostMapping(path = "/header") public String header(@RequestHeader(name = "a", defaultValue = "20") int a, @RequestHeader(name = "b", defaultValue = "bobo") String b, @RequestHeader(name = "c", defaultValue = "30") Integer c) { return "Hello " + a + b + c; } @GetMapping("/query") public String query(@RequestParam(name = "a", defaultValue = "20") int a, @RequestParam(name = "b", defaultValue = "bobo") String b, @RequestParam(name = "c", defaultValue = "40") Integer c, @RequestParam(name = "d") int d) { return "Hello " + a + b + c + d; } @GetMapping("/query2") public String query2(@RequestParam(name = "e", required = false) int e, @RequestParam(name = "a", defaultValue = "20") int a, @RequestParam(name = "b", defaultValue = "bobo") String b, @RequestParam(name = "c", defaultValue = "40") Integer c, @Min(value = 20) @Max(value = 30) @RequestParam(name = "d", required = false) int d) { return "Hello " + a + b + c + d + e; } @GetMapping("/query3") public String query3(@RequestParam("a") @Min(value = 20) int a, @RequestParam(name = "b", required = false) String b) { return "Hello " + a + b; } @PostMapping("/javaprimitiveint") public String springJavaPrimitiveInt(@RequestParam(name = "a", required = false) int a, @RequestParam(name = "b", defaultValue = "bobo") String b) { return "Hello " + a + b; } @PostMapping("/javaprimitivenumber") public String springJavaPrimitiveNumber(@RequestParam(name = "a", required = false) float a, @RequestParam(name = "b", required = false) boolean b) { return "Hello " + a + b; } @PostMapping("/javaprimitivestr") public String springJavaPrimitiveStr(@RequestParam(name = "a", required = false) int a, @RequestParam(name = "b", required = false) String b) { if (b == null || b.equals("")) { return "Hello"; } return "Hello " + b + a; } @PostMapping("/javaprimitivecomb") public String springJavaPrimitiveCombination(@RequestParam(name = "a", required = false) Integer a, @RequestParam(name = "b", required = false) Float b) { return "Hello " + a + b; } @PostMapping("/allprimitivetypes") public String allprimitivetypes(@RequestParam(name = "pBoolean", required = false) boolean pBoolean, @RequestParam(name = "pChar", required = false) char pChar, @RequestParam(name = "pByte", required = false) byte pByte, @RequestParam(name = "pShort", required = false) short pShort, @RequestParam(name = "pInt", required = false) int pInt, @RequestParam(name = "pLong", required = false) long pLong, @RequestParam(name = "pFloat", required = false) float pFloat, @RequestParam(name = "pDouble", required = false) double pDouble, @RequestParam(name = "pDoubleWrap", required = false) Double pDoubleWrap) { return "Hello " + pBoolean + "," + pChar + "," + pByte + "," + pShort + "," + pInt + "," + pLong + "," + pFloat + "," + pDouble + "," + pDoubleWrap; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "TransportSchema") @RequestMapping(path = "/transport") public class TransportSchema { @GetMapping(path = "/restTransport") @Transport(name = CoreConst.RESTFUL) public boolean restTransport(InvocationContext invocation) { return CoreConst.RESTFUL.equals(((Invocation) invocation).getTransportName()); } @GetMapping(path = "/highwayTransport") @Transport(name = CoreConst.HIGHWAY) public boolean highwayTransport(InvocationContext invocation) { return CoreConst.HIGHWAY.equals(((Invocation) invocation).getTransportName()); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; @RestSchema(schemaId = "UploadSchema") @RequestMapping(path = "/upload", produces = MediaType.APPLICATION_JSON_VALUE) public class UploadSchema { @PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String fileUpload(@RequestPart(name = "files") List files) { try { String fileName = UUID.randomUUID().toString(); List savedFiles = new ArrayList<>(); int index = 0; for (MultipartFile file : files) { File tempFile = new File("random-server-" + fileName + index); savedFiles.add(tempFile); file.transferTo(tempFile); index++; } savedFiles.forEach(File::delete); return "success"; } catch (IOException e) { return "failed"; } } @GetMapping(path = "/isServerStartUpSuccess") public boolean isServerStartUpSuccess() { return TestMgr.isSuccess(); } @PostMapping(path = "/fileUploadMultiRpc", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String fileUploadMultiRpc(@RequestPart(name = "files") MultipartFile[] files) { return "fileUploadMulti success, and fileNum is " + files.length; } @PostMapping(path = "/uploadFileRequestPartAttribute", produces = MediaType.TEXT_PLAIN_VALUE) public String uploadFileRequestPartAttribute(@RequestPart(name = "file") MultipartFile file, @RequestPart(name = "attribute") String attribute) throws IOException { try (InputStream is = file.getInputStream()) { return attribute + " " + IOUtils.toString(is, StandardCharsets.UTF_8); } } @PostMapping(path = "/uploadFileRequestPartAttributeList", produces = MediaType.TEXT_PLAIN_VALUE) public String uploadFileRequestPartAttributeList(@RequestPart(name = "files") List files, @RequestPart(name = "attributes") List attributes) throws IOException { StringBuilder result = new StringBuilder(); for (MultipartFile file : files) { try (InputStream is = file.getInputStream()) { result.append(IOUtils.toString(is, StandardCharsets.UTF_8)); } } for (String attribute : attributes) { result.append(attribute); } return result.toString(); } @PostMapping(path = "/uploadFileAndAttribute", produces = MediaType.TEXT_PLAIN_VALUE) public String uploadFileAndAttribute(@RequestPart(name = "file") MultipartFile file, @RequestAttribute(name = "attribute") String attribute) throws IOException { try (InputStream is = file.getInputStream()) { return attribute + " " + IOUtils.toString(is, StandardCharsets.UTF_8); } } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/WeakSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.server; import java.util.List; import org.apache.servicecomb.demo.model.SpecialNameModel; import org.apache.servicecomb.demo.server.GenericsModel; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.Operation; @RestSchema(schemaId = "weakSpringmvc") @RequestMapping(path = "/weakSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) public class WeakSpringmvc { @GetMapping(path = "/diffNames") @Operation(summary = "differentName", operationId = "differentName") public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) { return a * 2 + b; } @GetMapping(path = "/genericParams") @Operation(summary = "genericParams", operationId = "genericParams") public List> genericParams(@RequestParam("code") int code, @RequestBody List> names) { return names; } @GetMapping(path = "/genericParamsModel") @Operation(summary = "genericParamsModel", operationId = "genericParamsModel") public GenericsModel genericParamsModel(@RequestParam("code") int code, @RequestBody GenericsModel model) { return model; } @GetMapping(path = "/specialNameModel") @Operation(summary = "specialNameModel", operationId = "specialNameModel") public SpecialNameModel specialNameModel(@RequestParam("code") int code, @RequestBody SpecialNameModel model) { return model; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/EarlyConsumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.third; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.provider.pojo.RpcReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * see: https://github.com/apache/servicecomb-java-chassis/issues/2534 */ @Component public class EarlyConsumer implements BootListener { private static final Logger LOGGER = LoggerFactory.getLogger(EarlyConsumer.class); @RpcReference(microserviceName = "third", schemaId = "heartbeat") private HealthSchema healthSchema; private volatile boolean stopped = false; public EarlyConsumer() { new Thread(() -> { while (!stopped) { LOGGER.info("calling service"); try { healthSchema.heartbeat(); } catch (Throwable e) { // ignore error } try { Thread.sleep(500); } catch (Throwable e) { // ignore error } } }).start(); } @Override public void onAfterRegistry(BootEvent event) { stopped = true; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HealthSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.third; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/v1") public interface HealthSchema { @GetMapping(value = "/heartbeat") void heartbeat(); } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HeartBeatService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.third; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "health", schemaInterface = HealthSchema.class) public class HeartBeatService implements HealthSchema { @Override public void heartbeat() { } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/NormalConsumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.third; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.pojo.RpcReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * see: https://github.com/apache/servicecomb-java-chassis/issues/2534 */ @Component public class NormalConsumer implements BootListener { private static final Logger LOGGER = LoggerFactory.getLogger(NormalConsumer.class); @RpcReference(microserviceName = "third", schemaId = "heartbeat") private HealthSchema healthSchema; @Override public void onAfterRegistry(BootListener.BootEvent event) { try { LOGGER.info("calling service after register"); healthSchema.heartbeat(); LOGGER.info("heartbeat succ"); } catch (Throwable e) { TestMgr.failed("3rd invoke failed", e); throw e; } } @Override public int getOrder() { // 比ThirdServiceRegister晚 return 0; } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/Register.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.springmvc.third; import java.util.List; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; /** * see: https://github.com/apache/servicecomb-java-chassis/issues/2534 */ @Configuration public class Register { @Autowired Environment environment; @Bean public RegistryBean thirdRegistryBean() { String endpoint; if (environment.getProperty("servicecomb.test.vert.transport", boolean.class, true)) { endpoint = "rest://localhost:8080?sslEnabled=false&urlPrefix=%2Fapi"; } else { endpoint = "rest://localhost:8080?sslEnabled=false"; } return new RegistryBean().addSchemaInterface("heartbeat", HealthSchema.class) .setAppId("springmvctest") .setServiceName("third") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(List.of(endpoint))))); } } ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.springmvc.server.ResponseOKExceptionConverter ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.demo.springmvc.server.MyStrategyFactory ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # test not supported configuration # service_description.initialStatus service_description: initialStatus: TESTING servicecomb: service: application: springmvctest name: springmvc version: 0.0.3 registry: sc: address: http://127.0.0.1:9980,http://127.0.0.1:30100 autodiscovery: true healthCheckIntervalInSeconds: 3 pollIntervalInMillis: 10000 watch: true uploads: directory: target http: dispatcher: rest: pattern: "/api/(.*)" rest: address: 0.0.0.0:8080?sslEnabled=false server: compression: true highway: address: 0.0.0.0:7070?sslEnabled=true invocation: timeout: check: enabled: true strategy: processing-time SpringMVCCommonSchemaInterface: testInvocationTimeout: timeout: 1000 testInvocationTimeoutWithInvocation: timeout: 1000 flowcontrol: strategy: MyStrategy Provider: qps: enabled: true global: limit: 10 bucket: 30 tracing: enabled: true samplingRate: 0.5 datacenter: name: myDC region: my-Region availableZone: my-Zone codec.printErrorMessage: true # For old testing loadbalance: isolation: errorThresholdPercentage: 20 #########SSL options # open jdk 8 now TLSv1.3 not available # ssl.protocols: TLSv1.3 # ssl.ciphers: TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384 ssl.protocols: TLSv1.2 ssl.authPeer: true ssl.checkCN.host: true #########certificates config ssl.trustStore: trust.jks ssl.trustStoreType: JKS ssl.trustStoreValue: Changeme_123 ssl.keyStore: server.p12 ssl.keyStoreType: PKCS12 ssl.keyStoreValue: Changeme_123 ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom vertx.disableFileCPResolving: false # false: create the .vertx directory, true: do not create ================================================ FILE: demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.springmvc.server.CodeFirstSpringmvcForSchema version: 1.0.0 servers: - url: /forSchema paths: /uploadFile: post: operationId: uploadAwardFile parameters: - name: fileType in: query required: true schema: type: string - name: zoneId in: query required: true schema: type: string requestBody: content: multipart/form-data: schema: type: object properties: file: type: string format: binary responses: "200": description: response of 200 content: application/json: schema: type: boolean components: {} ================================================ FILE: demo/demo-zeroconfig-registry/README.md ================================================ # Introduction This demo tests zeroconfig. * server run in docker * client run in docker, and find server instances through zeroconfig, find schemas through schema discovery * edge run in docker, and find server instances through zeroconfig, find schemas through schema discovery * tests integration tests run in host machine, find client/edge instances through local, and schemas from schema discovery. ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml ================================================ 4.0.0 demo-zeroconfig-registry-client Java Chassis::Demo::ZeroConfig Registry Client org.apache.servicecomb.demo demo-zeroconfig-registry 3.4.0-SNAPSHOT org.apache.servicecomb.demo.zeroconfig.client.ClientApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb registry-zero-config org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.client; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ClientApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(ClientApplication.class).web(WebApplicationType.SERVLET) .build().run(args); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.client; import java.util.Date; public class ClientModel { private Date updateDate; public Date getUpdateDate() { return updateDate; } public void setUpdateDate(Date updateDate) { this.updateDate = updateDate; } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.client; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.vertx.core.json.JsonObject; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ClientServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ClientServerEndpoint { @RpcReference(microserviceName = "demo-zeroconfig-registry-server", schemaId = "ServerEndpoint") private IServerEndpoint serverEndpoint; @RpcReference(microserviceName = "demo-zeroconfig-registry-server", schemaId = "RpcEndpoint") private IRpcEndpoint rpcEndpoint; private DiscoveryManager discoveryManager; @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { return serverEndpoint.getName(name); } @GetMapping(path = "/getRegisteredMicroservice") public Set getRegisteredMicroservice() { boolean result = true; List microserviceList = discoveryManager .findServiceInstances("demo-zeroconfig-registry", "demo-zeroconfig-registry-client"); if (microserviceList.size() != 2) { // local and zero config both have one instance result = false; } microserviceList = discoveryManager .findServiceInstances("demo-zeroconfig-registry", "demo-zeroconfig-registry-server"); if (microserviceList.size() != 1) { result = false; } if (result) { Set names = new HashSet<>(); names.add("demo-zeroconfig-registry-client"); names.add("demo-zeroconfig-registry-server"); return names; } return Collections.emptySet(); } @PostMapping(path = "/jsonObject") public JsonObject jsonObject(@RequestBody JsonObject jsonObject) { JsonObject param = new JsonObject(); param.put("map", jsonObject); JsonObject message = rpcEndpoint.getJsonObject(param); JsonObject inner = new JsonObject(); inner.put("map", message); return inner; } @PostMapping(path = "/getString") public String getString(@RawJsonRequestBody String jsonString) { return jsonString; } @PostMapping(path = "/postModel") public ClientModel postModel(@RequestBody ClientModel clientModel) { return clientModel; } @GetMapping(path = "/contextMapper") public String contextMapper(@RequestHeader("gatewayHeader") String gatewayHeader, @RequestHeader("clientHeader") String clientHeader, @RequestParam("gatewayQuery") String gatewayQuery, @RequestParam("clientQuery") String clientQuery) { InvocationContext context = ContextUtils.getInvocationContext(); if (gatewayHeader.equals(context.getContext("context-gateway-header")) && clientHeader.equals(context.getContext("context-client-header")) && gatewayQuery.equals(context.getContext("context-gateway-query")) && clientQuery.equals(context.getContext("context-client-query"))) { return "success"; } return "fail"; } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/GovernanceEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.client; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestOperations; import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "GovernanceEndpoint") @RequestMapping("/governance") public class GovernanceEndpoint { private static final String SERVER = "servicecomb://demo-zeroconfig-registry-server"; private RestOperations restTemplate = RestTemplateBuilder.create(); private int count = 0; @GetMapping("/hello") public String hello() { return restTemplate.getForObject(SERVER + "/governance/hello", String.class); } @GetMapping("/helloRpc") public String helloRpc() { return restTemplate.getForObject(SERVER + "/governance/hello", String.class); } @GetMapping("/noPrefixRetry") public String noPrefixRetry(@RequestParam(name = "invocationID") String invocationID) { return restTemplate .getForObject(SERVER + "/noPrefixRetry?invocationID={1}", String.class, invocationID); } @GetMapping("/retry") public String retry(@RequestParam(name = "invocationID") String invocationID) { return restTemplate .getForObject(SERVER + "/governance/retry?invocationID={1}", String.class, invocationID); } @GetMapping("/retryRpc") public String retryRpc(@RequestParam(name = "invocationID") String invocationID) { return restTemplate .getForObject(SERVER + "/governance/retryRpc?invocationID={1}", String.class, invocationID); } @GetMapping("/circuitBreaker") public String circuitBreaker() throws Exception { count++; if (count % 3 == 0) { return "ok"; } throw new InvocationException(Status.SERVICE_UNAVAILABLE, "test error"); } @GetMapping("/bulkhead") public String bulkhead() { return restTemplate.getForObject(SERVER + "/governance/hello", String.class); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IRpcEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.client; import io.vertx.core.json.JsonObject; public interface IRpcEndpoint { JsonObject getJsonObject(JsonObject message); String getString(String message); } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.client; public interface IServerEndpoint { String getName(String name); } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8082 servicecomb: service: application: demo-zeroconfig-registry name: demo-zeroconfig-registry-client version: 0.0.2 rest: address: 0.0.0.0:8082 highway: address: 0.0.0.0:8084 config: client: # for testing dynamic configuration # serverUri: http://127.0.0.1:30113 refreshMode: 0 # refresh_interval: 5000 # refreshPort: 30114 governance: GovernanceEndpoint.helloRpc: matchType: rpc demo-zeroconfig-registry-server.GovernanceEndpoint.retryRpc: matchType: rpc matchGroup: demo-rateLimiting: | matches: - apiPath: exact: "/governance/hello" demo-rateLimiting-rpc: | matches: - apiPath: exact: "GovernanceEndpoint.helloRpc" demo-retry: | matches: - apiPath: exact: "/governance/retry" demo-retry-no-prefix: | matches: - apiPath: exact: "/noPrefixRetry" demo-retry-rpc: | matches: - apiPath: exact: "demo-zeroconfig-registry-server.GovernanceEndpoint.retryRpc" demo-circuitBreaker: | matches: - apiPath: exact: "/governance/circuitBreaker" demo-bulkhead: | matches: - apiPath: exact: "/governance/bulkhead" rateLimiting: demo-rateLimiting: | rate: 10 ## services is optional in configuration file services: demo-zeroconfig-registry-client demo-rateLimiting-rpc: | rate: 10 ## services is optional in configuration file services: demo-zeroconfig-registry-client retry: demo-retry: | maxAttempts: 3 ## services is optional in configuration file services: demo-zeroconfig-registry-client demo-retry-no-prefix: | maxAttempts: 3 ## services is optional in configuration file services: demo-zeroconfig-registry-client demo-retry-rpc: | maxAttempts: 3 ## services is optional in configuration file services: demo-zeroconfig-registry-client circuitBreaker: demo-circuitBreaker: | minimumNumberOfCalls: 10 slidingWindowSize: 10 failureRateThreshold: 20 ## services is optional in configuration file services: demo-zeroconfig-registry-client bulkhead: demo-bulkhead: | maxConcurrentCalls: 5 ## services is optional in configuration file services: demo-zeroconfig-registry-client context: headerContextMapper: | clientHeader: context-client-header queryContextMapper: | clientQuery: context-client-query ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml ================================================ 4.0.0 demo-zeroconfig-registry-edge Java Chassis::Demo::ZeroConfig Registry Edge org.apache.servicecomb.demo demo-zeroconfig-registry 3.4.0-SNAPSHOT org.apache.servicecomb.demo.zeroconfig.edge.EdgeApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb edge-core org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-zero-config org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.edge; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class EdgeApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(EdgeApplication.class).web(WebApplicationType.NONE).build().run(args); SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); invoker.latch.await(10, TimeUnit.SECONDS); TestMgr.check(invoker.result, "hello"); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { System.exit(1); } } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/SelfServiceInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.edge; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component("SelfServiceInvoker") public class SelfServiceInvoker implements BootListener { interface IServerEndpoint { String getName(String name); } @RpcReference(microserviceName = "demo-zeroconfig-registry-edge", schemaId = "ServerEndpoint") IServerEndpoint endpoint; public CountDownLatch latch = new CountDownLatch(1); public String result = ""; public void onAfterRegistry(BootEvent event) { result = endpoint.getName("hello"); latch.countDown(); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.edge; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/edge/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8888 servicecomb: service: application: demo-zeroconfig-registry name: demo-zeroconfig-registry-edge version: 0.0.2 rest: address: 0.0.0.0:8888 filter-chains: enabled: true http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: client: prefixSegmentCount: 0 path: "/register/url/prefix/.*" microserviceName: demo-zeroconfig-registry-client versionRule: 0+ references: transport: demo-zeroconfig-registry-client.ClientServerEndpoint.contextMapper: rest context: headerContextMapper: | gatewayHeader: context-gateway-header queryContextMapper: | gatewayQuery: context-gateway-query ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml ================================================ 4.0.0 demo-zeroconfig-registry-server Java Chassis::Demo::ZeroConfig Registry Server org.apache.servicecomb.demo demo-zeroconfig-registry 3.4.0-SNAPSHOT org.apache.servicecomb.demo.zeroconfig.server.ServerApplication jakarta.ws.rs jakarta.ws.rs-api org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb registry-zero-config org.apache.servicecomb.demo demo-schema org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin org.commonjava.maven.plugins directory-maven-plugin ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.server; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = "GovernanceEndpoint") @RequestMapping("/governance") public class GovernanceEndpoint { private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceEndpoint.class); private Map retryTimes = new HashMap<>(); @GetMapping("/hello") public String sayHello() { return "Hello world!"; } @GetMapping("/retry") @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String retry(@RequestParam(name = "invocationID") String invocationID) { LOGGER.info("invoke service: {}", invocationID); retryTimes.putIfAbsent(invocationID, 0); retryTimes.put(invocationID, retryTimes.get(invocationID) + 1); int retry = retryTimes.get(invocationID); if (retry == 3) { return "try times: " + retry; } throw new InvocationException(502, "retry", "retry"); } @GetMapping("/retryRpc") @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String retryRpc(@RequestParam(name = "invocationID") String invocationID) { return retry(invocationID); } @GetMapping("/circuitBreaker") public String circuitBreaker() { throw new InvocationException(Status.SERVICE_UNAVAILABLE, ""); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.server; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @RestSchema(schemaId = "GovernanceNoPrefixEndpoint") @RequestMapping("/") public class GovernanceNoPrefixEndpoint { private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceNoPrefixEndpoint.class); private Map retryTimes = new HashMap<>(); @GetMapping("/noPrefixRetry") @ApiResponses({ @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = ""), @ApiResponse(responseCode = "502", content = @Content(schema = @Schema(implementation = String.class)), description = "")}) public String noPrefixRetry(@RequestParam(name = "invocationID") String invocationID) { LOGGER.info("invoke service: {}", invocationID); retryTimes.putIfAbsent(invocationID, 0); retryTimes.put(invocationID, retryTimes.get(invocationID) + 1); int retry = retryTimes.get(invocationID); if (retry == 3) { return "try times: " + retry; } throw new InvocationException(502, "retry", "retry"); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/RpcEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.server; import org.apache.servicecomb.provider.pojo.RpcSchema; import io.vertx.core.json.JsonObject; @RpcSchema(schemaId = "RpcEndpoint") public class RpcEndpoint { public JsonObject getJsonObject(JsonObject message) { JsonObject inner = new JsonObject(); inner.put("map", message); return inner; } public String getString(String message) { return message; } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/SelfServiceInvoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.server; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.stereotype.Component; @Component("SelfServiceInvoker") public class SelfServiceInvoker implements BootListener { interface IServerEndpoint { String getName(String name); } @RpcReference(microserviceName = "demo-zeroconfig-registry-server", schemaId = "ServerEndpoint") IServerEndpoint endpoint; public CountDownLatch latch = new CountDownLatch(1); public String result = ""; public void onAfterRegistry(BootEvent event) { result = endpoint.getName("hello"); latch.countDown(); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.server; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ServerApplication { public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(ServerApplication.class).web(WebApplicationType.SERVLET).build().run(args); SelfServiceInvoker invoker = BeanUtils.getBean("SelfServiceInvoker"); invoker.latch.await(10, TimeUnit.SECONDS); TestMgr.check(invoker.result, "hello"); TestMgr.summary(); if (!TestMgr.errors().isEmpty()) { System.exit(1); } } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.server; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- server: port: 8080 servicecomb: service: application: demo-zeroconfig-registry name: demo-zeroconfig-registry-server version: 0.0.2 rest: address: 0.0.0.0:8080 filter-chains: enabled: true ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml ================================================ 4.0.0 demo-zeroconfig-registry-tests Java Chassis::Demo::ZeroConfig Registry Tests org.apache.servicecomb.demo demo-zeroconfig-registry 3.4.0-SNAPSHOT org.apache.servicecomb.demo.zeroconfig.tests.Application org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb registry-local org.apache.servicecomb.demo demo-schema docker io.fabric8 docker-maven-plugin demo-zeroconfig-registry-server:${project.version} demo-zeroconfig-registry-server /maven/maven/demo-zeroconfig-registry-server-${project.version}.jar Service information is shown below 8080 8080:8080 demo-zeroconfig-registry-client:${project.version} demo-zeroconfig-registry-client /maven/maven/demo-zeroconfig-registry-client-${project.version}.jar Service information is shown below 8082 8082:8082 demo-zeroconfig-registry-edge:${project.version} demo-zeroconfig-registry-edge /maven/maven/demo-zeroconfig-registry-edge-${project.version}.jar Service information is shown below 8888 8888:8888 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/Application.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.tests; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class Application { private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); public static void main(final String[] args) throws Exception { new SpringApplicationBuilder().sources(Application.class).web(WebApplicationType.NONE) .build().run(args); runTest(); TestMgr.summary(); LOGGER.info("-------------- last time updated checks(maybe more/less): 662 -------------"); if (!TestMgr.errors().isEmpty()) { throw new IllegalStateException("tests failed"); } } private static void runTest() throws Exception { CategorizedTestCaseRunner .runCategorizedTestCase("demo-zeroconfig-registry-server"); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ClientModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.tests; import java.util.Date; public class ClientModel { private Date updateDate; public Date getUpdateDate() { return updateDate; } public void setUpdateDate(Date updateDate) { this.updateDate = updateDate; } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.tests; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; @Component public class GovernanceTest implements CategorizedTestCase { String url = "servicecomb://demo-zeroconfig-registry-client/governance"; RestOperations template = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { testCircuitBreaker(); testBulkhead(); testRateLimitingRest(); testRateLimitingRpc(); testRetryRest(); testRetryRestNoPrefix(); testRetryRpc(); } private void testRetryRestNoPrefix() { testRetry("/noPrefixRetry"); } private void testRetryRest() { testRetry("/retry"); } private void testRetryRpc() { testRetry("/retryRpc"); } private void testRetry(String operation) { String invocationID = UUID.randomUUID().toString(); String result = template.getForObject(url + operation + "?invocationID={1}", String.class, invocationID); TestMgr.check(result, "try times: 3"); } private void testCircuitBreaker() throws Exception { CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { String result = template.getForObject(url + "/circuitBreaker", String.class); if (!"ok".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { if ("InvocationException: code=429;msg=CommonExceptionData [message=circuitBreaker is open.]" .equals(e.getMessage())) { expectedFailed.set(true); } else if ("InvocationException: code=503;msg=CommonExceptionData [message=test error]" .equals(e.getMessage())) { // by pass } else { notExpectedFailed.set(true); } } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(true, expectedFailed.get()); TestMgr.check(false, notExpectedFailed.get()); } private void testBulkhead() throws Exception { CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { String result = template.getForObject(url + "/bulkhead", String.class); if (!"Hello world!".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { if (!"InvocationException: code=429;msg=CommonExceptionData [message=bulkhead is full and does not permit further calls.]" .equals(e.getMessage())) { notExpectedFailed.set(true); } expectedFailed.set(true); } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(true, expectedFailed.get()); TestMgr.check(false, notExpectedFailed.get()); } private void testRateLimitingRest() throws Exception { testRateLimiting("/hello"); } private void testRateLimitingRpc() throws Exception { testRateLimiting("/helloRpc"); } private void testRateLimiting(String operation) throws Exception { CountDownLatch latch = new CountDownLatch(100); AtomicBoolean expectedFailed = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { String name = "t-" + i + "-" + j; new Thread(name) { public void run() { try { String result = template.getForObject(url + operation, String.class); if (!"Hello world!".equals(result)) { notExpectedFailed.set(true); } } catch (Exception e) { if (!"InvocationException: code=429;msg=CommonExceptionData [message=rate limited.]" .equals(e.getMessage())) { notExpectedFailed.set(true); } expectedFailed.set(true); } latch.countDown(); } }.start(); } Thread.sleep(100); } latch.await(20, TimeUnit.SECONDS); TestMgr.check(true, expectedFailed.get()); TestMgr.check(false, notExpectedFailed.get()); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.tests; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import io.vertx.core.json.JsonObject; @Component public class ServerTest implements CategorizedTestCase { RestOperations template = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { testServerGetName(); testGetAllMicroservice(); testJsonObject(); testString(); testDateForEdge(); testContextMapper(); } private void testContextMapper() throws URISyntaxException { MultiValueMap headers = new HttpHeaders(); headers.add("clientHeader", "v1"); headers.add("gatewayHeader", "v2"); RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("cse://demo-zeroconfig-registry-edge/register/url/prefix/contextMapper?clientQuery=v3&" + "gatewayQuery=v4")); // test two times to check different transport(only use rest) ResponseEntity response = template.exchange(requestEntity, String.class); TestMgr.check(response.getBody(), "success"); response = template.exchange(requestEntity, String.class); TestMgr.check(response.getBody(), "success"); } @SuppressWarnings("unchecked") private void testDateForEdge() { for (int i = 0; i < 3; i++) { ClientModel clientModelReq = new ClientModel(); Date date = new Date(1663590135202L); clientModelReq.setUpdateDate(date); Map response = template .postForObject( "cse://demo-zeroconfig-registry-edge" + "/register/url/prefix/postModel", clientModelReq, Map.class); Object result = response.get("updateDate"); // TODO: highway and rest Date field will give different result // we can not change this now, because it is incompatible if (result instanceof Long) { TestMgr.check(response.get("updateDate"), 1663590135202L); } else { TestMgr.check(response.get("updateDate"), "2022-09-19T12:22:15.202+00:00"); } } } private void testServerGetName() throws Exception { // invoke demo-zeroconfig-registry-client TestMgr.check("world", template .getForObject( "cse://demo-zeroconfig-registry-client/register/url/prefix/getName?name=world", String.class)); // invoke demo-zeroconfig-registry-edge // create many threads to test event-loop not blocking int thread = 32; CountDownLatch latch = new CountDownLatch(thread); for (int i = 0; i < thread; i++) { new Thread(() -> { for (int j = 0; j < 20; j++) { try { TestMgr.check("world", template .getForObject( "cse://demo-zeroconfig-registry-edge/register/url/prefix/getName?name=world", String.class)); } catch (Exception e) { TestMgr.failed("test failed", e); } } latch.countDown(); }).start(); } latch.await(); } @SuppressWarnings("rawTypes") private void testGetAllMicroservice() { // invoke demo-zeroconfig-registry-client TestMgr.check("2", template .exchange( "cse://demo-zeroconfig-registry-client" + "/register/url/prefix/getRegisteredMicroservice", HttpMethod.GET, null, new ParameterizedTypeReference>() { }).getBody().size()); // invoke demo-zeroconfig-registry-edge TestMgr.check("2", template .exchange( "cse://demo-zeroconfig-registry-edge" + "/register/url/prefix/getRegisteredMicroservice", HttpMethod.GET, null, new ParameterizedTypeReference>() { }).getBody().size()); } private void testJsonObject() { JsonObject in = new JsonObject(); JsonObject inner = new JsonObject(); //调用者需要按照swagger传参 inner.put("hello", "world"); in.put("map", inner); JsonObject result = template .postForObject( "cse://demo-zeroconfig-registry-client" + "/register/url/prefix/jsonObject", in, JsonObject.class); TestMgr.check(inner.toString(), result.toString()); TestMgr.check(result.getString("hello"), "world"); } private void testString() { String in = "{\"hello\":\"world\"}"; String result = template .postForObject( "cse://demo-zeroconfig-registry-client" + "/register/url/prefix/getString", in, String.class); TestMgr.check(in, result); } } ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/application.yml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-zeroconfig-registry name: demo-zeroconfig-registry-tests version: 0.0.2 handler: chain: Consumer: default: loadbalance loadbalance: filter: isolation: enabled: false ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/logback.xml ================================================ %d [%level] [%thread] - %msg (%F:%L\)%n ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/ClientServerEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.zeroconfig.client.ClientServerEndpoint version: 1.0.0 servers: - url: /register/url/prefix paths: /contextMapper: get: operationId: contextMapper parameters: - name: gatewayHeader in: header required: true schema: type: string - name: clientHeader in: header required: true schema: type: string - name: gatewayQuery in: query required: true schema: type: string - name: clientQuery in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /getName: get: operationId: getName parameters: - name: name in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /getRegisteredMicroservice: get: operationId: getRegisteredMicroservice responses: "200": description: response of 200 content: application/json: schema: uniqueItems: true type: array items: type: string /getString: post: operationId: getString requestBody: content: application/json: schema: type: string required: true x-raw-json: true x-name: jsonString responses: "200": description: response of 200 content: application/json: schema: type: string /jsonObject: post: operationId: jsonObject requestBody: content: application/json: schema: $ref: '#/components/schemas/JsonObject' required: true x-name: jsonObject responses: "200": description: response of 200 content: application/json: schema: $ref: '#/components/schemas/JsonObject' /postModel: post: operationId: postModel requestBody: content: application/json: schema: $ref: '#/components/schemas/ClientModel' required: true x-name: clientModel responses: "200": description: response of 200 content: application/json: schema: $ref: '#/components/schemas/ClientModel' components: schemas: JsonObject: type: object properties: map: type: object additionalProperties: type: object empty: type: boolean x-java-class: io.vertx.core.json.JsonObject ClientModel: type: object properties: updateDate: type: string format: date-time x-java-class: org.apache.servicecomb.demo.zeroconfig.client.ClientModel ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/GovernanceEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.zeroconfig.client.GovernanceEndpoint version: 1.0.0 servers: - url: /governance paths: /bulkhead: get: operationId: bulkhead responses: "200": description: response of 200 content: application/json: schema: type: string /circuitBreaker: get: operationId: circuitBreaker responses: "200": description: response of 200 content: application/json: schema: type: string /hello: get: operationId: hello responses: "200": description: response of 200 content: application/json: schema: type: string /helloRpc: get: operationId: helloRpc responses: "200": description: response of 200 content: application/json: schema: type: string /noPrefixRetry: get: operationId: noPrefixRetry parameters: - name: invocationID in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /retry: get: operationId: retry parameters: - name: invocationID in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /retryRpc: get: operationId: retryRpc parameters: - name: invocationID in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-client/SchemaDiscoveryEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.schemadiscovery.SchemaDiscoveryEndpoint version: 1.0.0 servers: - url: /v1/schema/discovery paths: /getSchema: get: operationId: getSchema parameters: - name: schemaId in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.demo.zeroconfig.client.ClientServerEndpoint version: 1.0.0 servers: - url: /register/url/prefix paths: /contextMapper: get: operationId: contextMapper parameters: - name: gatewayHeader in: header required: true schema: type: string - name: clientHeader in: header required: true schema: type: string - name: gatewayQuery in: query required: true schema: type: string - name: clientQuery in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /getName: get: operationId: getName parameters: - name: name in: query required: true schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /getRegisteredMicroservice: get: operationId: getRegisteredMicroservice responses: "200": description: response of 200 content: application/json: schema: uniqueItems: true type: array items: type: string /getString: post: operationId: getString requestBody: content: application/json: schema: type: string required: true x-raw-json: true x-name: jsonString responses: "200": description: response of 200 content: application/json: schema: type: string /jsonObject: post: operationId: jsonObject requestBody: content: application/json: schema: $ref: '#/components/schemas/JsonObject' required: true x-name: jsonObject responses: "200": description: response of 200 content: application/json: schema: $ref: '#/components/schemas/JsonObject' /postModel: post: operationId: postModel requestBody: content: application/json: schema: $ref: '#/components/schemas/ClientModel' required: true x-name: clientModel responses: "200": description: response of 200 content: application/json: schema: $ref: '#/components/schemas/ClientModel' components: schemas: JsonObject: type: object properties: map: type: object additionalProperties: type: object empty: type: boolean x-java-class: io.vertx.core.json.JsonObject ClientModel: type: object properties: updateDate: type: string format: date-time x-java-class: org.apache.servicecomb.demo.zeroconfig.client.ClientModel ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/SchemaDiscoveryEndpoint.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.schemadiscovery.SchemaDiscoveryEndpoint version: 1.0.0 servers: - url: /v1/schema/discovery paths: /getSchema: get: operationId: getSchema parameters: - name: schemaId in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- demo-zeroconfig-registry-client: - id: "001" version: "0.0.2" appid: demo-zeroconfig-registry schemaIds: - ClientServerEndpoint - SchemaDiscoveryEndpoint - GovernanceEndpoint instances: - endpoints: - rest://localhost:8082 demo-zeroconfig-registry-edge: - id: "002" version: "0.0.2" appid: demo-zeroconfig-registry schemaIds: - ClientServerEndpoint - SchemaDiscoveryEndpoint instances: - endpoints: - rest://localhost:8888 ================================================ FILE: demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/test/java/org/apache/servicecomb/demo/zeroconfig/tests/ZeroConfigRegistryIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.demo.zeroconfig.tests; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ZeroConfigRegistryIT { @BeforeEach public void setUp() throws Exception { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { Application.main(new String[0]); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/demo-zeroconfig-registry/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-zeroconfig-registry Java Chassis::Demo::ZeroConfig Registry pom demo-zeroconfig-registry-server demo-zeroconfig-registry-edge demo-zeroconfig-registry-client demo-zeroconfig-registry-tests org.apache.servicecomb solution-basic org.apache.servicecomb foundation-test-scaffolding compile org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-api org.apache.logging.log4j log4j-core ================================================ FILE: demo/demo-zookeeper/README.md ================================================ # Notice This integration tests is designed for Zookeeper registry and configuration. And extra test cases include: * Test cases related to SpringMVC annotations that demo-springmvc can not cover. ================================================ FILE: demo/demo-zookeeper/consumer/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-zookeeper 3.4.0-SNAPSHOT zookeeper-consumer Java Chassis::Demo::Zookeeper::CONSUMER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-zookeeper org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.ServerWebSocket; import io.vertx.core.http.WebSocket; @RestSchema(schemaId = "ClientWebsocketController") @RequestMapping(path = "/ws") public class ClientWebsocketController { interface ProviderService { WebSocket websocket(); } @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") private ProviderService providerService; @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) public void websocket(ServerWebSocket serverWebsocket) { WebSocket providerWebSocket = providerService.websocket(); providerWebSocket.closeHandler(v -> serverWebsocket.close()); providerWebSocket.textMessageHandler(m -> { System.out.println("send message " + m); serverWebsocket.writeTextMessage(m); }); serverWebsocket.textMessageHandler(m -> { System.out.println("receive message " + m); providerWebSocket.writeTextMessage(m); }); } } ================================================ FILE: demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ConsumerController") @RequestMapping(path = "/") public class ConsumerController { @RpcReference(schemaId = "ProviderController", microserviceName = "provider") private ProviderService providerService; // consumer service which delegate the implementation to provider service. @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } @GetMapping("/getConfig") public String getConfig(@RequestParam("key") String key) { return providerService.getConfig(key); } } ================================================ FILE: demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") private IHeaderParamWithListSchemaSpringMvc provider; @Override public String headerListDefault(List headerList) { return provider.headerListDefault(headerList); } @Override public String headerListCSV(List headerList) { return provider.headerListCSV(headerList); } @Override public String headerListMULTI(List headerList) { return provider.headerListMULTI(headerList); } @Override public String headerListSSV(List headerList) { return provider.headerListSSV(headerList); } @Override public String headerListPIPES(List headerList) { return provider.headerListPIPES(headerList); } } ================================================ FILE: demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") public class ConsumerReactiveStreamController { interface ProviderReactiveStreamController { Publisher sseString(); Publisher sseModel(); } @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") ProviderReactiveStreamController controller; public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") @Transport(name = CoreConst.RESTFUL) public Publisher sseString() { return controller.sseString(); } @GetMapping("/sseModel") @Transport(name = CoreConst.RESTFUL) public Publisher sseModel() { return controller.sseModel(); } } ================================================ FILE: demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface ProviderService { String sayHello(String name); String getConfig(String key); } ================================================ FILE: demo/demo-zookeeper/consumer/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-zookeeper version: 0.0.1 name: consumer properties: group: red registry: zk: enabled: true connectString: 127.0.0.1:2181 rest: address: 0.0.0.0:9092?websocketEnabled=true server: websocket-prefix: /ws highway: address: 0.0.0.0:7092 ================================================ FILE: demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zookeeper/gateway/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-zookeeper 3.4.0-SNAPSHOT zookeeper-gateway Java Chassis::Demo::Zookeeper::GATEWAY jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb edge-core org.apache.servicecomb registry-zookeeper org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-zookeeper/gateway/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-zookeeper version: 0.0.1 name: gateway registry: zk: enabled: true connectString: 127.0.0.1:2181 rest: address: 0.0.0.0:9090?websocketEnabled=true server: websocket-prefix: /ws http: dispatcher: edge: default: enabled: false url: enabled: true pattern: /(.*) mappings: consumer: prefixSegmentCount: 0 path: "/.*" microserviceName: consumer versionRule: 0.0.0+ websocket: mappings: consumer: prefixSegmentCount: 0 path: "/ws/.*" microserviceName: consumer versionRule: 0.0.0+ ================================================ FILE: demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zookeeper/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-parent 3.4.0-SNAPSHOT demo-zookeeper Java Chassis::Demo::Zookeeper pom org.apache.servicecomb.demo demo-schema org.apache.servicecomb solution-basic org.apache.logging.log4j log4j-slf4j-impl org.apache.logging.log4j log4j-core org.apache.logging.log4j log4j-api provider consumer gateway test-client ================================================ FILE: demo/demo-zookeeper/provider/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-zookeeper 3.4.0-SNAPSHOT zookeeper-provider Java Chassis::Demo::Zookeeper::PROVIDER jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb registry-zookeeper org.apache.servicecomb config-zookeeper io.reactivex.rxjava3 rxjava org.springframework.boot spring-boot-maven-plugin docker io.fabric8 docker-maven-plugin ${project.artifactId}:${project.version} ${project.artifactId} eclipse-temurin:17-jre-jammy 7070 8080 tar ${root.basedir}/assembly/assembly.xml java -Xmx128m $JAVA_OPTS -jar $JAR_PATH build package build io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { @Override public String headerListDefault(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListCSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListMULTI(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListSSV(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } @Override public String headerListPIPES(List headerList) { return headerList == null ? "null" : headerList.size() + ":" + headerList; } } ================================================ FILE: demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.zookeeper.ZookeeperClient; import org.apache.servicecomb.config.zookeeper.ZookeeperConfig; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController implements InitializingBean { private Environment environment; private ZookeeperConfig zookeeperConfig; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; this.zookeeperConfig = new ZookeeperConfig(environment); } // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { return "Hello " + name; } @GetMapping("/getConfig") public String getConfig(@RequestParam("key") String key) { return environment.getProperty(key); } @Override public void afterPropertiesSet() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConfig.getConnectString(), zookeeperConfig.getSessionTimeoutMillis(), zookeeperConfig.getConnectionTimeoutMillis(), new ExponentialBackoffRetry(1000, 3)); client.start(); client.blockUntilConnected(10, TimeUnit.SECONDS); String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = ZookeeperConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; } String path = String.format(ZookeeperClient.PATH_ENVIRONMENT, env); if (client.checkExists().forPath(path + "/config.properties") != null) { client.delete().forPath(path + "/config.properties"); } client.create().creatingParentsIfNeeded(). forPath(path + "/config.properties", "key1=1\nkey2=2".getBytes(StandardCharsets.UTF_8)); path = String.format(ZookeeperClient.PATH_VERSION, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment)); if (client.checkExists().forPath(path + "/config.properties") != null) { client.delete().forPath(path + "/config.properties"); } client.create().creatingParentsIfNeeded(). forPath(path + "/config.properties", "key2=3\nkey3=4".getBytes(StandardCharsets.UTF_8)); path = String.format(ZookeeperClient.PATH_TAG, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment), zookeeperConfig.getInstanceTag()); if (client.checkExists().forPath(path + "/config.properties") != null) { client.delete().forPath(path + "/config.properties"); } client.create().creatingParentsIfNeeded(). forPath(path + "/config.properties", "key2=3\nkey3=5".getBytes(StandardCharsets.UTF_8)); client.close(); } } ================================================ FILE: demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.reactivestreams.Publisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.reactivex.rxjava3.core.Flowable; @RestSchema(schemaId = "ReactiveStreamController") @RequestMapping(path = "/") @Transport(name = CoreConst.RESTFUL) public class ReactiveStreamController { public static class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") public Publisher sseString() { return Flowable.fromArray("a", "b", "c"); } @GetMapping("/sseModel") public Publisher sseModel() { return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) .map(item -> new Model("jack", item.intValue())); } } ================================================ FILE: demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.ServerWebSocket; @RestSchema(schemaId = "WebsocketController") @RequestMapping(path = "/ws") public class WebsocketController { @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) public void websocket(ServerWebSocket serverWebsocket) { AtomicInteger receiveCount = new AtomicInteger(0); CountDownLatch startSend = new CountDownLatch(1); serverWebsocket.textMessageHandler(s -> { if ("start".equals(s)) { startSend.countDown(); serverWebsocket.writeTextMessage("started"); return; } receiveCount.getAndIncrement(); }); serverWebsocket.closeHandler((v) -> System.out.println("closed")); new Thread(() -> { try { startSend.await(30, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } serverWebsocket.writeTextMessage("hello"); for (int i = 0; i < 5; i++) { serverWebsocket.writeTextMessage("hello " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } serverWebsocket.writeTextMessage("total " + receiveCount.get()); serverWebsocket.close(); }).start(); } } ================================================ FILE: demo/demo-zookeeper/provider/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations servicecomb: service: application: demo-zookeeper version: 0.0.1 name: provider properties: group: green registry: zk: connectString: 127.0.0.1:2181 config: zk: connectString: 127.0.0.1:2181 instance-tag: config-demo rest: address: 0.0.0.0:9094?websocketEnabled=true server: websocket-prefix: /ws highway: address: 0.0.0.0:7094 cors: enabled: true origin: "*" allowCredentials: false allowedMethod: "*" maxAge: 3600 ================================================ FILE: demo/demo-zookeeper/provider/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zookeeper/test-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb.demo demo-zookeeper 3.4.0-SNAPSHOT zookeeper-test-client Java Chassis::Demo::Zookeeper::TEST-CLIENT jar org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb.demo demo-schema org.apache.servicecomb registry-local docker io.fabric8 docker-maven-plugin zookeeper:3.8.3 zookeeper alias binding to port 2181 zookeeper.port:2181 zookeeper-provider:${project.version} zookeeper-provider alias -Dservicecomb.registry.zk.connectString=zookeeper:2181 -Dservicecomb.config.zk.connectString=zookeeper:2181 /maven/maven/zookeeper-provider-${project.version}.jar zookeeper:zookeeper ServiceComb is ready 9094 9094:9094 zookeeper-consumer:${project.version} zookeeper-consumer alias -Dservicecomb.registry.zk.connectString=zookeeper:2181 /maven/maven/zookeeper-consumer-${project.version}.jar zookeeper:zookeeper ServiceComb is ready 9092 9092:9092 zookeeper-gateway:${project.version} zookeeper-gateway alias -Dservicecomb.registry.zk.connectString=zookeeper:2181 /maven/maven/zookeeper-gateway-${project.version}.jar zookeeper:zookeeper ServiceComb is ready 9090 9090:9090 start pre-integration-test start stop post-integration-test stop io.fabric8 docker-maven-plugin ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; public interface Config { String GATEWAY_URL = "http://localhost:9090"; } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HeaderParamWithListSchemaIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHeaderListDefault(); testHeaderListMulti(); testHeaderListCSV(); testHeaderListSSV(); testHeaderListPipes(); } // default to multi private void testHeaderListDefault() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListPipes() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a|b|c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListSSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a b c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListCSV() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a,b,c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); headers.add("headerList", "a, b, c"); entity = new HttpEntity<>(headers); result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } private void testHeaderListMulti() { MultiValueMap headers = new HttpHeaders(); headers.add("headerList", "a"); headers.add("headerList", "b"); headers.add("headerList", "c"); HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); TestMgr.check("3:[a, b, c]", result); } } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @Component public class HelloWorldIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @Override public void testRestTransport() throws Exception { testHelloWorld(); testGetConfig(); } private void testGetConfig() { String result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); TestMgr.check("1", result); result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); TestMgr.check("3", result); result = template .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); TestMgr.check("5", result); } private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); TestMgr.check("Hello World", result); // test trace id added MultiValueMap headers = new HttpHeaders(); headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); HttpEntity entity = new HttpEntity(headers); ResponseEntity response = template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); TestMgr.check("Hello World", response.getBody()); } } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class ReactiveStreamIT implements CategorizedTestCase { @Autowired @Qualifier("reactiveStreamProvider") ReactiveStreamClient reactiveStreamProvider; @Autowired @Qualifier("reactiveStreamGateway") ReactiveStreamClient reactiveStreamGateway; @Override public void testRestTransport() throws Exception { testSseString(reactiveStreamProvider); testSseModel(reactiveStreamProvider); testSseString(reactiveStreamGateway); testSseModel(reactiveStreamGateway); } private void testSseModel(ReactiveStreamClient client) throws Exception { Publisher result = client.sseModel(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(Model s) { buffer.append(s.getName()).append(s.getAge()); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); } private void testSseString(ReactiveStreamClient client) throws Exception { Publisher result = client.sseString(); StringBuilder buffer = new StringBuilder(); CountDownLatch countDownLatch = new CountDownLatch(1); result.subscribe(new Subscriber<>() { Subscription subscription; @Override public void onSubscribe(Subscription s) { subscription = s; subscription.request(1); } @Override public void onNext(String s) { buffer.append(s); subscription.request(1); } @Override public void onError(Throwable t) { subscription.cancel(); countDownLatch.countDown(); } @Override public void onComplete() { countDownLatch.countDown(); } }); countDownLatch.await(10, TimeUnit.SECONDS); TestMgr.check("abc", buffer.toString()); } } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.TestMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class TestClientApplication { private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); public static void main(String[] args) throws Exception { try { new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); } public static void run() throws Exception { CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); } } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.List; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.annotation.Transport; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.apache.servicecomb.provider.pojo.Invoker; import org.reactivestreams.Publisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.vertx.core.http.WebSocket; @Configuration public class ThirdSvcConfiguration { @RequestMapping(path = "/ws") public interface WebsocketClient { @PostMapping("/websocket") @Transport(name = CoreConst.WEBSOCKET) WebSocket websocket(); } @RequestMapping(path = "/") public interface ReactiveStreamClient { class Model { private String name; private int age; public Model() { } public Model(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public Model setAge(int age) { this.age = age; return this; } public String getName() { return name; } public Model setName(String name) { this.name = name; return this; } } @GetMapping("/sseString") Publisher sseString(); @GetMapping("/sseModel") Publisher sseModel(); } @Bean public RegistryBean providerServiceBean() { return new RegistryBean() .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) .setAppId("demo-zookeeper") .setServiceName("provider") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of( new Instance().setEndpoints(List.of("rest://localhost:9094"))))); } @Bean public RegistryBean gatewayServiceBean() { return new RegistryBean() .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) .addSchemaInterface("WebsocketController", WebsocketClient.class) .setAppId("demo-zookeeper") .setServiceName("gateway") .setVersion("0.0.1") .setInstances(new Instances().setInstances(List.of( new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); } @Bean("reactiveStreamProvider") public ReactiveStreamClient reactiveStreamProvider() { return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); } @Bean("reactiveStreamGateway") public ReactiveStreamClient reactiveStreamGateway() { return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); } @Bean public WebsocketClient gatewayWebsocketClient() { return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); } } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import io.vertx.core.http.WebSocket; @Component public class WebsocketIT implements CategorizedTestCase { @Autowired private WebsocketClient websocketClient; @Override public void testRestTransport() throws Exception { StringBuffer sb = new StringBuffer(); AtomicBoolean closed = new AtomicBoolean(false); CountDownLatch latchStarted = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1); WebSocket webSocket = websocketClient.websocket(); webSocket.textMessageHandler(s -> { if ("started".equals(s)) { latchStarted.countDown(); return; } sb.append(s); sb.append(" "); webSocket.writeTextMessage(s); }); webSocket.closeHandler(v -> { closed.set(true); latch.countDown(); }); webSocket.writeTextMessage("start"); int i = 0; for (; i < 10; i++) { if (!latchStarted.await(3, TimeUnit.SECONDS)) { webSocket.writeTextMessage("start"); continue; } break; } TestMgr.check(i < 10, true); latch.await(30, TimeUnit.SECONDS); TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); TestMgr.check(closed.get(), true); } } ================================================ FILE: demo/demo-zookeeper/test-client/src/main/resources/application.yml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: service: application: demo-zookeeper name: test-client version: 0.0.1 rest: address: 0.0.0.0:9097 # should be same with server.port to use web container ================================================ FILE: demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml ================================================ ================================================ FILE: demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.samples; import org.apache.servicecomb.demo.TestMgr; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = TestClientApplication.class) public class ZookeeperIT { private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperIT.class); @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test public void clientGetsNoError() throws Exception { try { TestClientApplication.run(); } catch (Exception e) { TestMgr.failed("test case run failed", e); LOGGER.error("-------------- test failed -------------"); LOGGER.error("", e); LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); Assertions.assertTrue(TestMgr.errors().isEmpty()); } } ================================================ FILE: demo/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default org.apache.servicecomb.demo demo-parent Java Chassis::Demo pom demo-schema demo-pojo demo-filter demo-jaxrs demo-springmvc demo-crossapp demo-register-url-prefix demo-local-registry demo-multi-registries demo-zeroconfig-registry demo-spring-boot-transport demo-edge demo-multiple demo-multi-service-center demo-cse-v1 demo-cse-v2 demo-nacos demo-zookeeper demo-consul org.apache.servicecomb.demo demo-schema ${project.version} org.apache.servicecomb.demo all-client ${project.version} org.apache.servicecomb.demo pojo-server ${project.version} org.apache.servicecomb.demo jaxrs-server ${project.version} org.apache.servicecomb.demo springmvc-server ${project.version} org.apache.servicecomb.demo crossapp-server ${project.version} org.apache.servicecomb.demo multiple-server ${project.version} org.apache.servicecomb.demo multiple-client ${project.version} org.apache.servicecomb.demo pojo-client ${project.version} org.apache.servicecomb.demo jaxrs-client ${project.version} org.apache.servicecomb.demo springmvc-client ${project.version} org.apache.servicecomb.demo crossapp-client ${project.version} org.apache.servicecomb foundation-test-scaffolding ${project.version} org.junit.jupiter junit-jupiter compile org.commonjava.maven.plugins directory-maven-plugin 1.0 maven-deploy-plugin true maven-install-plugin false org.apache.maven.plugins maven-jar-plugin assembly package true lib/ ${demo.main} org.commonjava.maven.plugins directory-maven-plugin directories directory-of initialize root.basedir org.apache.servicecomb.demo demo-parent org.commonjava.maven.plugins directory-maven-plugin docker org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} ${jacoco.failsafe.argLine} integration-test verify ================================================ FILE: dependencies/bom/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-dependencies-parent 3.4.0-SNAPSHOT java-chassis-bom Java Chassis::Bom pom org.apache.servicecomb config-center-client ${project.version} org.apache.servicecomb config-clients-common ${project.version} org.apache.servicecomb config-kie-client ${project.version} org.apache.servicecomb dashboard-client ${project.version} org.apache.servicecomb http-client-common ${project.version} org.apache.servicecomb service-center-client ${project.version} org.apache.servicecomb common-access-log ${project.version} org.apache.servicecomb common-protobuf ${project.version} org.apache.servicecomb common-rest ${project.version} org.apache.servicecomb java-chassis-core ${project.version} org.apache.servicecomb config-apollo ${project.version} org.apache.servicecomb config-cc ${project.version} org.apache.servicecomb config-kie ${project.version} org.apache.servicecomb config-nacos ${project.version} org.apache.servicecomb config-zookeeper ${project.version} org.apache.servicecomb config-consul ${project.version} org.apache.servicecomb edge-core ${project.version} org.apache.servicecomb foundation-common ${project.version} org.apache.servicecomb foundation-config ${project.version} org.apache.servicecomb foundation-metrics ${project.version} org.apache.servicecomb foundation-protobuf ${project.version} org.apache.servicecomb foundation-registry ${project.version} org.apache.servicecomb foundation-spi ${project.version} org.apache.servicecomb foundation-ssl ${project.version} org.apache.servicecomb foundation-test-scaffolding ${project.version} test org.apache.servicecomb foundation-vertx ${project.version} org.apache.servicecomb servicecomb-governance ${project.version} org.apache.servicecomb handler-fault-injection ${project.version} org.apache.servicecomb handler-flowcontrol-qps ${project.version} org.apache.servicecomb handler-governance ${project.version} org.apache.servicecomb handler-loadbalance ${project.version} org.apache.servicecomb handler-publickey-auth ${project.version} org.apache.servicecomb handler-router ${project.version} org.apache.servicecomb handler-tracing-zipkin ${project.version} org.apache.servicecomb metrics-core ${project.version} org.apache.servicecomb metrics-prometheus ${project.version} org.apache.servicecomb provider-jaxrs ${project.version} org.apache.servicecomb provider-pojo ${project.version} org.apache.servicecomb provider-rest-common ${project.version} org.apache.servicecomb provider-springmvc ${project.version} org.apache.servicecomb registry-lightweight ${project.version} org.apache.servicecomb registry-local ${project.version} org.apache.servicecomb registry-service-center ${project.version} org.apache.servicecomb registry-zero-config ${project.version} org.apache.servicecomb registry-nacos ${project.version} org.apache.servicecomb registry-zookeeper ${project.version} org.apache.servicecomb registry-etcd ${project.version} org.apache.servicecomb config-etcd ${project.version} org.apache.servicecomb registry-consul ${project.version} org.apache.servicecomb solution-basic ${project.version} org.apache.servicecomb java-chassis-spring-boot-starter-servlet ${project.version} org.apache.servicecomb java-chassis-spring-boot-starter-standalone ${project.version} org.apache.servicecomb servicestage ${project.version} org.apache.servicecomb dashboard ${project.version} org.apache.servicecomb darklaunch ${project.version} org.apache.servicecomb swagger-generator-core ${project.version} org.apache.servicecomb swagger-generator-jaxrs ${project.version} org.apache.servicecomb swagger-generator-spring-data ${project.version} org.apache.servicecomb swagger-generator-springmvc ${project.version} org.apache.servicecomb swagger-invocation-core ${project.version} org.apache.servicecomb swagger-invocation-jaxrs ${project.version} org.apache.servicecomb swagger-invocation-springmvc ${project.version} org.apache.servicecomb swagger-invocation-validator ${project.version} org.apache.servicecomb tracing-common ${project.version} org.apache.servicecomb tracing-zipkin ${project.version} org.apache.servicecomb transport-common ${project.version} org.apache.servicecomb transport-highway ${project.version} org.apache.servicecomb transport-rest-client ${project.version} org.apache.servicecomb transport-rest-servlet ${project.version} org.apache.servicecomb transport-rest-vertx ${project.version} ================================================ FILE: dependencies/default/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-dependencies-parent 3.4.0-SNAPSHOT java-chassis-dependencies Java Chassis::Dependencies::Default pom 1.2.2 3.27.7 4.3.0 6.3.0 1.11.0 2.21.0 2.6 3.20.0 1.3.5 1.15.0 5.9.0 1.0.3 3.0.2 1.17.12 1.17.13 33.5.0-jre 5.1.0 1.3 2.2.2 9.1.0.Final 4.5.14 1.5.18 2.20.1 1.6.0 1.3.2 1 1.5 4.0.2 1.34 4.13.2 5.13.4 1.2.17 2.25.3 3.9.12 1.16.4 5.21.0 5.2.0 5.15.0 3.1.1 0.3.0 0.16.0 3.23.4 1.8.0 2.2.27 1.0.4 1.7.0 2.7.18 3.1.12 1.0.0 0.13.2 6.0.0 1.7.36 2.5 2.2.41 5.0.8 3.5.1 3.4.0 0.8.6 1.8.0 ${basedir}/../.. com.fasterxml.jackson jackson-bom ${jackson.version} import pom org.mockito mockito-bom ${mockito.version} import pom org.mockito mockito-inline ${mockito-inline.version} test com.github.seanyinx unit-scaffolding ${seanyinx.version} test org.apache.curator curator-x-discovery ${curator.version} org.apache.curator curator-framework ${curator.version} org.apache.curator curator-recipes ${curator.version} com.google.code.findbugs jsr305 ${findbugs-jsr305.version} provided com.google.guava guava ${guava.version} * * com.google.guava failureaccess ${failureaccess.version} com.google.inject guice ${guice.version} aopalliance aopalliance com.google.inject.extensions guice-assistedinject ${guice.version} com.google.protobuf protobuf-java ${protobuf.version} test commons-beanutils commons-beanutils ${commons-beanutils.version} commons-io commons-io ${commons-io.version} commons-lang commons-lang ${commons-lang.version} commons-logging commons-logging ${commons-logging.version} org.apache.maven maven-model ${maven-model.version} io.micrometer micrometer-bom ${micrometer.version} pom import io.prometheus simpleclient_bom ${prometheus.version} pom import io.protostuff protostuff-core ${protostuff.version} io.protostuff protostuff-parser ${protostuff-parser.version} org.abego.treelayout org.abego.treelayout.core com.google.inject.extensions guice-multibindings io.protostuff protostuff-runtime ${protostuff.version} io.swagger.core.v3 swagger-core-jakarta ${swagger.version} io.reactivex.rxjava3 rxjava ${rxjava.version} org.reactivestreams reactive-streams ${reactive-streams.version} io.zipkin.brave brave-bom ${brave.version} pom import io.zipkin.reporter2 zipkin-reporter-bom ${zipkin-reporter.version} io.zipkin.zipkin2 zipkin ${zipkin.version} jakarta.servlet jakarta.servlet-api ${servlet-api.version} com.sun.activation jakarta.activation ${activation.version} javax.annotation javax.annotation-api ${javax-annotation.version} javax.inject javax.inject ${javax-inject.version} javax.resource connector-api ${javax-resource.version} junit junit ${junit.version} test org.junit junit-bom ${junit5.version} pom import org.apache.commons commons-lang3 ${commons-lang3.version} org.apache.commons commons-text ${commons-text.version} org.apache.httpcomponents httpclient ${httpcomponents.version} org.apache.logging.log4j log4j-api ${log4j2.version} org.apache.logging.log4j log4j-core ${log4j2.version} org.apache.logging.log4j log4j-slf4j-impl ${log4j2.version} org.awaitility awaitility ${awaitility.version} test org.assertj assertj-core ${assertj.version} test org.glassfish.jersey.core jersey-common ${jersey.version} org.glassfish.jersey.core jersey-client ${jersey.version} org.hamcrest hamcrest-all ${hamcrest.version} test org.hamcrest hamcrest-core ${hamcrest.version} test org.hibernate.validator hibernate-validator ${hibernate-validator.version} org.hibernate.validator hibernate-validator-annotation-processor ${hibernate-validator.version} org.hdrhistogram HdrHistogram ${hdr-histogram.version} org.jmockit jmockit ${jmockit.version} test org.slf4j slf4j-api ${slf4j.version} io.vertx vertx-dependencies ${vertx.version} pom import org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.yaml snakeyaml ${snakeyaml.version} com.alibaba.nacos nacos-client ${nacos-client.version} io.github.resilience4j resilience4j-bom ${resilience4j.versioin} pom import org.java-websocket Java-WebSocket ${java-websocket.version} org.apache.servicecomb java-chassis-bom ${project.version} pom import io.etcd jetcd-core ${jetcd-core.version} org.kiwiproject consul-client ${consul-client.version} ================================================ FILE: dependencies/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis 3.4.0-SNAPSHOT java-chassis-dependencies-parent Java Chassis::Dependencies pom bom default ================================================ FILE: distribution/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default apache-servicecomb-java-chassis-distribution Java Chassis::Distribution pom org.apache.servicecomb config-center-client org.apache.servicecomb config-clients-common org.apache.servicecomb config-kie-client org.apache.servicecomb dashboard-client org.apache.servicecomb http-client-common org.apache.servicecomb service-center-client org.apache.servicecomb registry-nacos org.apache.servicecomb common-access-log org.apache.servicecomb common-protobuf org.apache.servicecomb common-rest org.apache.servicecomb java-chassis-core org.apache.servicecomb config-apollo org.apache.servicecomb config-cc org.apache.servicecomb config-kie org.apache.servicecomb config-nacos org.apache.servicecomb edge-core org.apache.servicecomb foundation-common org.apache.servicecomb foundation-config org.apache.servicecomb foundation-metrics org.apache.servicecomb foundation-protobuf org.apache.servicecomb foundation-registry org.apache.servicecomb foundation-spi org.apache.servicecomb foundation-ssl org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb foundation-vertx org.apache.servicecomb servicecomb-governance org.apache.servicecomb handler-fault-injection org.apache.servicecomb handler-flowcontrol-qps org.apache.servicecomb handler-governance org.apache.servicecomb handler-loadbalance org.apache.servicecomb handler-publickey-auth org.apache.servicecomb handler-router org.apache.servicecomb handler-tracing-zipkin org.apache.servicecomb metrics-core org.apache.servicecomb metrics-prometheus org.apache.servicecomb provider-jaxrs org.apache.servicecomb provider-pojo org.apache.servicecomb provider-rest-common org.apache.servicecomb provider-springmvc org.apache.servicecomb registry-lightweight org.apache.servicecomb registry-local org.apache.servicecomb registry-service-center org.apache.servicecomb registry-zero-config org.apache.servicecomb solution-basic org.apache.servicecomb java-chassis-spring-boot-starter-servlet org.apache.servicecomb java-chassis-spring-boot-starter-standalone org.apache.servicecomb servicestage org.apache.servicecomb dashboard org.apache.servicecomb darklaunch org.apache.servicecomb swagger-generator-core org.apache.servicecomb swagger-generator-jaxrs org.apache.servicecomb swagger-generator-spring-data org.apache.servicecomb swagger-generator-springmvc org.apache.servicecomb swagger-invocation-core org.apache.servicecomb swagger-invocation-jaxrs org.apache.servicecomb swagger-invocation-springmvc org.apache.servicecomb swagger-invocation-validator org.apache.servicecomb tracing-common org.apache.servicecomb tracing-zipkin org.apache.servicecomb transport-common org.apache.servicecomb transport-highway org.apache.servicecomb transport-rest-client org.apache.servicecomb transport-rest-servlet org.apache.servicecomb transport-rest-vertx release maven-assembly-plugin bin package single src/assembly/bin.xml src package single src/assembly/src.xml ================================================ FILE: distribution/src/assembly/bin.xml ================================================ true ${project.build.finalName}-bin bin zip ../ ./ README_ZH.md README.md src/release ./ NOTICE LICENSE licenses/** true false ./libs runtime org.apache.servicecomb:* org.apache.servicecomb.archetypes:* ================================================ FILE: distribution/src/assembly/src.xml ================================================ src true ${project.build.finalName}-src zip .. **/* etc/eclipse-java-google-style.xml etc/intellij-java-google-style.xml **/eclipse-classes/** **/target/** **/build/** **/eclipse-classes/** **/.* **/.*/** *.enc *.gpg random_seed **/surefire* **/svn-commit* **/.idea/** **/*.iml **/*.ipr **/*.iws **/cobertura.ser **/node_modules/** ================================================ FILE: distribution/src/release/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ======================================================================= Apache ServiceComb Java Chassis Subcomponents: The Apache ServiceComb Java Chassis project contains subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the following licenses. ================================================================ For foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java ================================================================ This product bundles files from vertx which is licensed under the Apache License v2. For details, see https://github.com/vert-x3/vertx-web ================================================================ For swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java ================================================================ This product bundles files from swagger which is licensed under the Apache License v2. For details, see https://github.com/swagger-api/swagger-core ================================================================ For foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java ================================================================ This product bundles files from protostuff which is licensed under the Apache License v2. For details, see https://github.com/protostuff/protostuff ================================================ FILE: distribution/src/release/NOTICE ================================================ Apache ServiceComb Java Chassis Copyright 2017-2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ================================================ FILE: distribution/src/release/licenses/LICENSE-abego ================================================ [The "BSD license"] Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the abego Software GmbH nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: distribution/src/release/licenses/LICENSE-animalsniffer ================================================ The MIT License Copyright (c) 2009 codehaus.org. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-antlr ================================================ [The "BSD 3-clause license"] Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ===== MIT License for codepointat.js from https://git.io/codepointat MIT License for fromcodepoint.js from https://git.io/vDW1m Copyright Mathias Bynens Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-asm ================================================ ASM: a very small and fast Java bytecode manipulation framework Copyright (c) 2000-2011 INRIA, France Telecom All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: distribution/src/release/licenses/LICENSE-bouncycastle ================================================ Copyright (c) 2000 - 2017 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-cc0 ================================================ Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. ================================================ FILE: distribution/src/release/licenses/LICENSE-cddl ================================================ COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 1. Definitions. 1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. 1.3. "Covered Software" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. 1.4. "Executable" means the Covered Software in any form other than Source Code. 1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License. 1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. 1.7. "License" means this document. 1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means the Source Code and Executable form of any of the following: A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; B. Any new file that contains any part of the Original Software or previous Modification; or C. Any new file that is contributed or otherwise made available under the terms of this License. 1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License. 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. 1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants. 2.1. The Initial Developer Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. 2.2. Contributor Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Availability of Source Code. Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. 3.2. Modifications. The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. 3.3. Required Notices. You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. 3.4. Application of Additional Terms. You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.5. Distribution of Executable Versions. You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.6. Larger Works. You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. 4. Versions of the License. 4.1. New Versions. Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. 4.2. Effect of New Versions. You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. 4.3. Modified Versions. When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. 5. DISCLAIMER OF WARRANTY. COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 6. TERMINATION. 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. 6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. 7. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 8. U.S. GOVERNMENT END USERS. The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. 9. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. 10. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. ------------------------------------------------------------------------ NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. The GNU General Public License (GPL) Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. One line to give the program's name and a brief idea of what it does. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. # Certain source files distributed by Oracle America, Inc. and/or its affiliates are subject to the following clarification and special exception to the GPLv2, based on the GNU Project exception for its Classpath libraries, known as the GNU Classpath Exception, but only where Oracle has expressly included in the particular source file's header the words "Oracle designates this particular file as subject to the "Classpath" exception as provided by Oracle in the LICENSE file that accompanied this code." You should also note that Oracle includes multiple, independent programs in this software package. Some of those programs are provided under licenses deemed incompatible with the GPLv2 by the Free Software Foundation and others. For example, the package includes programs licensed under the Apache License, Version 2.0. Such programs are licensed to you under their original licenses. Oracle facilitates your further distribution of this package by adding the Classpath Exception to the necessary parts of its GPLv2 code, which permits you to use that code in combination with other independent modules not licensed under the GPLv2. However, note that this would not permit you to commingle code under an incompatible license with Oracle's GPLv2 licensed code by, for example, cutting and pasting such code into a file also containing Oracle's GPLv2 licensed code and then distributing the result. Additionally, if you were to remove the Classpath Exception from any of the files to which it applies and distribute the result, you would likely be required to license some or all of the other code in that distribution under the GPLv2 as well, and since the GPLv2 is incompatible with the license terms of some items included in the distribution by Oracle, removing the Classpath Exception could therefore effectively compromise your ability to further distribute the package. Proceed with caution and we recommend that you obtain the advice of a lawyer skilled in open source matters before removing the Classpath Exception or making modifications to this package which may subsequently be redistributed and/or involve the use of third party software. CLASSPATH EXCEPTION Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License version 2 cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. ================================================ FILE: distribution/src/release/licenses/LICENSE-checkerqual ================================================ MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-edl-v10 ================================================ Eclipse Distribution License - v 1.0 Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: distribution/src/release/licenses/LICENSE-epl-v10 ================================================ Eclipse Public License - v 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. ================================================ FILE: distribution/src/release/licenses/LICENSE-epl-v20 ================================================ Eclipse Public License - v 2.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS “Contribution” means: a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. “Contributor” means any person or entity that Distributes the Program. “Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. “Program” means the Contributions Distributed in accordance with this Agreement. “Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. “Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. “Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. “Distribute” means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. “Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. “Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). 3. REQUIREMENTS 3.1 If a Contributor Distributes the Program in any form, then: a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. 3.2 When the Program is Distributed as Source Code: a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and b) a copy of this Agreement must be included with each copy of the Program. 3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (‘notices’) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. Exhibit A – Form of Secondary Licenses Notice “This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}.” Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. ================================================ FILE: distribution/src/release/licenses/LICENSE-hamcrest ================================================ BSD License Copyright (c) 2000-2015 www.hamcrest.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Hamcrest nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: distribution/src/release/licenses/LICENSE-hdrhistogram ================================================ The code in this repository code was Written by Gil Tene, Michael Barker, and Matt Warren, and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ For users of this code who wish to consume it under the "BSD" license rather than under the public domain or CC0 contribution text mentioned above, the code found under this directory is *also* provided under the following license (commonly referred to as the BSD 2-Clause License). This license does not detract from the above stated release of the code into the public domain, and simply represents an additional license granted by the Author. ----------------------------------------------------------------------------- ** Beginning of "BSD 2-Clause License" text. ** Copyright (c) 2012, 2013, 2014, 2015, 2016 Gil Tene Copyright (c) 2014 Michael Barker Copyright (c) 2014 Matt Warren All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: distribution/src/release/licenses/LICENSE-icu4j ================================================ COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) Copyright © 1991-2018 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html. Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either (a) this copyright and permission notice appear with all copies of the Data Files or Software, or (b) this copyright and permission notice appear in associated Documentation. THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. --------------------- Third-Party Software Licenses This section contains third-party software notices and/or additional terms for licensed third-party software components included within ICU libraries. 1. ICU License - ICU 1.8.1 to ICU 57.1 COPYRIGHT AND PERMISSION NOTICE Copyright (c) 1995-2016 International Business Machines Corporation and others All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. All trademarks and registered trademarks mentioned herein are the property of their respective owners. 2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) # The Google Chrome software developed by Google is licensed under # the BSD license. Other software included in this distribution is # provided under other licenses, as set forth below. # # The BSD License # http://opensource.org/licenses/bsd-license.php # Copyright (C) 2006-2008, Google Inc. # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided with # the distribution. # Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # # The word list in cjdict.txt are generated by combining three word lists # listed below with further processing for compound word breaking. The # frequency is generated with an iterative training against Google web # corpora. # # * Libtabe (Chinese) # - https://sourceforge.net/project/?group_id=1519 # - Its license terms and conditions are shown below. # # * IPADIC (Japanese) # - http://chasen.aist-nara.ac.jp/chasen/distribution.html # - Its license terms and conditions are shown below. # # ---------COPYING.libtabe ---- BEGIN-------------------- # # /* # * Copyright (c) 1999 TaBE Project. # * Copyright (c) 1999 Pai-Hsiang Hsiao. # * All rights reserved. # * # * Redistribution and use in source and binary forms, with or without # * modification, are permitted provided that the following conditions # * are met: # * # * . Redistributions of source code must retain the above copyright # * notice, this list of conditions and the following disclaimer. # * . Redistributions in binary form must reproduce the above copyright # * notice, this list of conditions and the following disclaimer in # * the documentation and/or other materials provided with the # * distribution. # * . Neither the name of the TaBE Project nor the names of its # * contributors may be used to endorse or promote products derived # * from this software without specific prior written permission. # * # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # * OF THE POSSIBILITY OF SUCH DAMAGE. # */ # # /* # * Copyright (c) 1999 Computer Systems and Communication Lab, # * Institute of Information Science, Academia # * Sinica. All rights reserved. # * # * Redistribution and use in source and binary forms, with or without # * modification, are permitted provided that the following conditions # * are met: # * # * . Redistributions of source code must retain the above copyright # * notice, this list of conditions and the following disclaimer. # * . Redistributions in binary form must reproduce the above copyright # * notice, this list of conditions and the following disclaimer in # * the documentation and/or other materials provided with the # * distribution. # * . Neither the name of the Computer Systems and Communication Lab # * nor the names of its contributors may be used to endorse or # * promote products derived from this software without specific # * prior written permission. # * # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # * OF THE POSSIBILITY OF SUCH DAMAGE. # */ # # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, # University of Illinois # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 # # ---------------COPYING.libtabe-----END-------------------------------- # # # ---------------COPYING.ipadic-----BEGIN------------------------------- # # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science # and Technology. All Rights Reserved. # # Use, reproduction, and distribution of this software is permitted. # Any copy of this software, whether in its original form or modified, # must include both the above copyright notice and the following # paragraphs. # # Nara Institute of Science and Technology (NAIST), # the copyright holders, disclaims all warranties with regard to this # software, including all implied warranties of merchantability and # fitness, in no event shall NAIST be liable for # any special, indirect or consequential damages or any damages # whatsoever resulting from loss of use, data or profits, whether in an # action of contract, negligence or other tortuous action, arising out # of or in connection with the use or performance of this software. # # A large portion of the dictionary entries # originate from ICOT Free Software. The following conditions for ICOT # Free Software applies to the current dictionary as well. # # Each User may also freely distribute the Program, whether in its # original form or modified, to any third party or parties, PROVIDED # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear # on, or be attached to, the Program, which is distributed substantially # in the same form as set out herein and that such intended # distribution, if actually made, will neither violate or otherwise # contravene any of the laws and regulations of the countries having # jurisdiction over the User or the intended distribution itself. # # NO WARRANTY # # The program was produced on an experimental basis in the course of the # research and development conducted during the project and is provided # to users as so produced on an experimental basis. Accordingly, the # program is provided without any warranty whatsoever, whether express, # implied, statutory or otherwise. The term "warranty" used herein # includes, but is not limited to, any warranty of the quality, # performance, merchantability and fitness for a particular purpose of # the program and the nonexistence of any infringement or violation of # any right of any third party. # # Each user of the program will agree and understand, and be deemed to # have agreed and understood, that there is no warranty whatsoever for # the program and, accordingly, the entire risk arising from or # otherwise connected with the program is assumed by the user. # # Therefore, neither ICOT, the copyright holder, or any other # organization that participated in or was otherwise related to the # development of the program and their respective officials, directors, # officers and other employees shall be held liable for any and all # damages, including, without limitation, general, special, incidental # and consequential damages, arising out of or otherwise in connection # with the use or inability to use the program or any product, material # or result produced or otherwise obtained by using the program, # regardless of whether they have been advised of, or otherwise had # knowledge of, the possibility of such damages at any time during the # project or thereafter. Each user will be deemed to have agreed to the # foregoing by his or her commencement of use of the program. The term # "use" as used herein includes, but is not limited to, the use, # modification, copying and distribution of the program and the # production of secondary products from the program. # # In the case where the program, whether in its original form or # modified, was distributed or delivered to or received by a user from # any person, organization or entity other than ICOT, unless it makes or # grants independently of ICOT any specific warranty to the user in # writing, such person, organization or entity, will also be exempted # from and not be held liable to the user for any such damages as noted # above as far as the program is concerned. # # ---------------COPYING.ipadic-----END---------------------------------- 3. Lao Word Break Dictionary Data (laodict.txt) # Copyright (c) 2013 International Business Machines Corporation # and others. All Rights Reserved. # # Project: http://code.google.com/p/lao-dictionary/ # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt # (copied below) # # This file is derived from the above dictionary, with slight # modifications. # ---------------------------------------------------------------------- # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, # are permitted provided that the following conditions are met: # # # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. Redistributions in # binary form must reproduce the above copyright notice, this list of # conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # OF THE POSSIBILITY OF SUCH DAMAGE. # -------------------------------------------------------------------------- 4. Burmese Word Break Dictionary Data (burmesedict.txt) # Copyright (c) 2014 International Business Machines Corporation # and others. All Rights Reserved. # # This list is part of a project hosted at: # github.com/kanyawtech/myanmar-karen-word-lists # # -------------------------------------------------------------------------- # Copyright (c) 2013, LeRoy Benjamin Sharon # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: Redistributions of source code must retain the above # copyright notice, this list of conditions and the following # disclaimer. Redistributions in binary form must reproduce the # above copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # Neither the name Myanmar Karen Word Lists, nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -------------------------------------------------------------------------- 5. Time Zone Database ICU uses the public domain data and code derived from Time Zone Database for its time zone support. The ownership of the TZ database is explained in BCP 175: Procedure for Maintaining the Time Zone Database section 7. # 7. Database Ownership # # The TZ database itself is not an IETF Contribution or an IETF # document. Rather it is a pre-existing and regularly updated work # that is in the public domain, and is intended to remain in the # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do # not apply to the TZ Database or contributions that individuals make # to it. Should any claims be made and substantiated against the TZ # Database, the organization that is providing the IANA # Considerations defined in this RFC, under the memorandum of # understanding with the IETF, currently ICANN, may act in accordance # with all competent court orders. No ownership claims will be made # by ICANN or the IETF Trust on the database or the code. Any person # making a contribution to the database or code waives all rights to # future claims in that contribution or in the TZ Database. 6. Google double-conversion Copyright 2006-2011, the V8 project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: distribution/src/release/licenses/LICENSE-jcodings ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-joni ================================================ MIT License Copyright (c) 2017 JRuby Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-jopt ================================================ The MIT License Copyright (c) 2004-2016 Paul R. Holser, Jr. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-jsonp ================================================ 1. Definitions. 1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. 1.3. "Covered Software" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. 1.4. "Executable" means the Covered Software in any form other than Source Code. 1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License. 1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. 1.7. "License" means this document. 1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means the Source Code and Executable form of any of the following: A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; B. Any new file that contains any part of the Original Software or previous Modification; or C. Any new file that is contributed or otherwise made available under the terms of this License. 1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License. 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. 1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants. 2.1. The Initial Developer Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. 2.2. Contributor Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Availability of Source Code. Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. 3.2. Modifications. The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. 3.3. Required Notices. You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. 3.4. Application of Additional Terms. You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.5. Distribution of Executable Versions. You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.6. Larger Works. You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. 4. Versions of the License. 4.1. New Versions. Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. 4.2. Effect of New Versions. You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. 4.3. Modified Versions. When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. 5. DISCLAIMER OF WARRANTY. COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 6. TERMINATION. 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. 6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. 7. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 8. U.S. GOVERNMENT END USERS. The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. 9. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. 10. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. ================================================ FILE: distribution/src/release/licenses/LICENSE-jsoup ================================================ The MIT License Copyright (c) 2009-2018 Jonathan Hedley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-jsr311-api ================================================ COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 1. Definitions. 1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. 1.3. "Covered Software" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. 1.4. "Executable" means the Covered Software in any form other than Source Code. 1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License. 1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. 1.7. "License" means this document. 1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means the Source Code and Executable form of any of the following: A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; B. Any new file that contains any part of the Original Software or previous Modification; or C. Any new file that is contributed or otherwise made available under the terms of this License. 1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License. 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. 1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants. 2.1. The Initial Developer Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. 2.2. Contributor Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Availability of Source Code. Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. 3.2. Modifications. The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. 3.3. Required Notices. You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. 3.4. Application of Additional Terms. You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.5. Distribution of Executable Versions. You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.6. Larger Works. You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. 4. Versions of the License. 4.1. New Versions. Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. 4.2. Effect of New Versions. You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. 4.3. Modified Versions. When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. 5. DISCLAIMER OF WARRANTY. COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 6. TERMINATION. 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. 6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. 7. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 8. U.S. GOVERNMENT END USERS. The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. 9. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. 10. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. ------------------------------------------------------------------------ NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. The GNU General Public License (GPL) Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. One line to give the program's name and a brief idea of what it does. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. # Certain source files distributed by Oracle America, Inc. and/or its affiliates are subject to the following clarification and special exception to the GPLv2, based on the GNU Project exception for its Classpath libraries, known as the GNU Classpath Exception, but only where Oracle has expressly included in the particular source file's header the words "Oracle designates this particular file as subject to the "Classpath" exception as provided by Oracle in the LICENSE file that accompanied this code." You should also note that Oracle includes multiple, independent programs in this software package. Some of those programs are provided under licenses deemed incompatible with the GPLv2 by the Free Software Foundation and others. For example, the package includes programs licensed under the Apache License, Version 2.0. Such programs are licensed to you under their original licenses. Oracle facilitates your further distribution of this package by adding the Classpath Exception to the necessary parts of its GPLv2 code, which permits you to use that code in combination with other independent modules not licensed under the GPLv2. However, note that this would not permit you to commingle code under an incompatible license with Oracle's GPLv2 licensed code by, for example, cutting and pasting such code into a file also containing Oracle's GPLv2 licensed code and then distributing the result. Additionally, if you were to remove the Classpath Exception from any of the files to which it applies and distribute the result, you would likely be required to license some or all of the other code in that distribution under the GPLv2 as well, and since the GPLv2 is incompatible with the license terms of some items included in the distribution by Oracle, removing the Classpath Exception could therefore effectively compromise your ability to further distribute the package. Proceed with caution and we recommend that you obtain the advice of a lawyer skilled in open source matters before removing the Classpath Exception or making modifications to this package which may subsequently be redistributed and/or involve the use of third party software. CLASSPATH EXCEPTION Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License version 2 cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. ================================================ FILE: distribution/src/release/licenses/LICENSE-logback ================================================ Logback LICENSE --------------- Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights reserved. This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation or (per the licensee's choosing) under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. ================================================ FILE: distribution/src/release/licenses/LICENSE-mozilla-v20 ================================================ Mozilla Public License Version 2.0 1. Definitions 1.1. “Contributor” means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. “Contributor Version” means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution. 1.3. “Contribution” means Covered Software of a particular Contributor. 1.4. “Covered Software” means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. “Incompatible With Secondary Licenses” means that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. “Executable Form” means any form of the work other than Source Code Form. 1.7. “Larger Work” means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. “License” means this document. 1.9. “Licensable” means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. “Modifications” means any of the following: any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or any new file in Source Code Form that contains any Covered Software. 1.11. “Patent Claims” of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. “Secondary License” means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. “Source Code Form” means the form of the work preferred for making modifications. 1.14. “You” (or “Your”) means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: for any code that a Contributor has removed from Covered Software; or for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. 6. Disclaimer of Warranty Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer. 7. Limitation of Liability Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. 8. Litigation Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims. 9. Miscellaneous This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - “Incompatible With Secondary Licenses” Notice This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: distribution/src/release/licenses/LICENSE-protobuf ================================================ Copyright 2008 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Code generated by the Protocol Buffer compiler is owned by the owner of the input file used when generating it. This code is not standalone and requires a support library to be linked with it. This support library is itself covered by the above license. ================================================ FILE: distribution/src/release/licenses/LICENSE-slf4j ================================================ Copyright (c) 2004-2017 QOS.ch All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: distribution/src/release/licenses/LICENSE-woodstox-stax2-api ================================================ This copy of Stax2 API is licensed under the Simplified BSF License (also known as "2-clause BSD", or "FreeBSD License") See the License for details about distribution rights, and the specific rights regarding derivate works. You may obtain a copy of the License at: http://www.opensource.org/licenses/bsd-license.php with details of: = FasterXML.com = 2010- ================================================ FILE: distribution/src/release/licenses/NOTICE-apache-commons-codec ================================================ Apache Commons Codec Copyright 2002-2017 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java contains test data from http://aspell.net/test/orig/batch0.tab. Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) =============================================================================== The content of package org.apache.commons.codec.language.bm has been translated from the original php source code available at http://stevemorse.org/phoneticinfo.htm with permission from the original authors. Original source copyright: Copyright (c) 2008 Alexander Beider & Stephen P. Morse. ================================================ FILE: distribution/src/release/licenses/NOTICE-netty ================================================ The Netty Project ================= Please visit the Netty web site for more information: * http://netty.io/ Copyright 2014 The Netty Project The Netty Project licenses this file to you under the Apache License, version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Also, please refer to each LICENSE..txt file, which is located in the 'license' directory of the distribution file, for the license terms of the components that this product depends on. ------------------------------------------------------------------------------- This product contains the extensions to Java Collections Framework which has been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: * LICENSE: * license/LICENSE.jsr166y.txt (Public Domain) * HOMEPAGE: * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ This product contains a modified version of Robert Harder's Public Domain Base64 Encoder and Decoder, which can be obtained at: * LICENSE: * license/LICENSE.base64.txt (Public Domain) * HOMEPAGE: * http://iharder.sourceforge.net/current/java/base64/ This product contains a modified portion of 'Webbit', an event based WebSocket and HTTP server, which can be obtained at: * LICENSE: * license/LICENSE.webbit.txt (BSD License) * HOMEPAGE: * https://github.com/joewalnes/webbit This product contains a modified portion of 'SLF4J', a simple logging facade for Java, which can be obtained at: * LICENSE: * license/LICENSE.slf4j.txt (MIT License) * HOMEPAGE: * http://www.slf4j.org/ This product contains a modified portion of 'Apache Harmony', an open source Java SE, which can be obtained at: * NOTICE: * license/NOTICE.harmony.txt * LICENSE: * license/LICENSE.harmony.txt (Apache License 2.0) * HOMEPAGE: * http://archive.apache.org/dist/harmony/ This product contains a modified portion of 'jbzip2', a Java bzip2 compression and decompression library written by Matthew J. Francis. It can be obtained at: * LICENSE: * license/LICENSE.jbzip2.txt (MIT License) * HOMEPAGE: * https://code.google.com/p/jbzip2/ This product contains a modified portion of 'libdivsufsort', a C API library to construct the suffix array and the Burrows-Wheeler transformed string for any input string of a constant-size alphabet written by Yuta Mori. It can be obtained at: * LICENSE: * license/LICENSE.libdivsufsort.txt (MIT License) * HOMEPAGE: * https://github.com/y-256/libdivsufsort This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, which can be obtained at: * LICENSE: * license/LICENSE.jctools.txt (ASL2 License) * HOMEPAGE: * https://github.com/JCTools/JCTools This product optionally depends on 'JZlib', a re-implementation of zlib in pure Java, which can be obtained at: * LICENSE: * license/LICENSE.jzlib.txt (BSD style License) * HOMEPAGE: * http://www.jcraft.com/jzlib/ This product optionally depends on 'Compress-LZF', a Java library for encoding and decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: * LICENSE: * license/LICENSE.compress-lzf.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/ning/compress This product optionally depends on 'lz4', a LZ4 Java compression and decompression library written by Adrien Grand. It can be obtained at: * LICENSE: * license/LICENSE.lz4.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/jpountz/lz4-java This product optionally depends on 'lzma-java', a LZMA Java compression and decompression library, which can be obtained at: * LICENSE: * license/LICENSE.lzma-java.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/jponge/lzma-java This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression and decompression library written by William Kinney. It can be obtained at: * LICENSE: * license/LICENSE.jfastlz.txt (MIT License) * HOMEPAGE: * https://code.google.com/p/jfastlz/ This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data interchange format, which can be obtained at: * LICENSE: * license/LICENSE.protobuf.txt (New BSD License) * HOMEPAGE: * https://github.com/google/protobuf This product optionally depends on 'Bouncy Castle Crypto APIs' to generate a temporary self-signed X.509 certificate when the JVM does not provide the equivalent functionality. It can be obtained at: * LICENSE: * license/LICENSE.bouncycastle.txt (MIT License) * HOMEPAGE: * http://www.bouncycastle.org/ This product optionally depends on 'Snappy', a compression library produced by Google Inc, which can be obtained at: * LICENSE: * license/LICENSE.snappy.txt (New BSD License) * HOMEPAGE: * https://github.com/google/snappy This product optionally depends on 'JBoss Marshalling', an alternative Java serialization API, which can be obtained at: * LICENSE: * license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1) * HOMEPAGE: * http://www.jboss.org/jbossmarshalling This product optionally depends on 'Caliper', Google's micro- benchmarking framework, which can be obtained at: * LICENSE: * license/LICENSE.caliper.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/google/caliper This product optionally depends on 'Apache Commons Logging', a logging framework, which can be obtained at: * LICENSE: * license/LICENSE.commons-logging.txt (Apache License 2.0) * HOMEPAGE: * http://commons.apache.org/logging/ This product optionally depends on 'Apache Log4J', a logging framework, which can be obtained at: * LICENSE: * license/LICENSE.log4j.txt (Apache License 2.0) * HOMEPAGE: * http://logging.apache.org/log4j/ This product optionally depends on 'Aalto XML', an ultra-high performance non-blocking XML processor, which can be obtained at: * LICENSE: * license/LICENSE.aalto-xml.txt (Apache License 2.0) * HOMEPAGE: * http://wiki.fasterxml.com/AaltoHome This product contains a modified version of 'HPACK', a Java implementation of the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: * LICENSE: * license/LICENSE.hpack.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/twitter/hpack This product contains a modified portion of 'Apache Commons Lang', a Java library provides utilities for the java.lang API, which can be obtained at: * LICENSE: * license/LICENSE.commons-lang.txt (Apache License 2.0) * HOMEPAGE: * https://commons.apache.org/proper/commons-lang/ This product contains the Maven wrapper scripts from 'Maven Wrapper', that provides an easy way to ensure a user has everything necessary to run the Maven build. * LICENSE: * license/LICENSE.mvn-wrapper.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/takari/maven-wrapper ================================================ FILE: distribution/src/release/licenses/NOTICE-prometheus ================================================ Prometheus instrumentation library for JVM applications Copyright 2012-2015 The Prometheus Authors This product includes software developed at Boxever Ltd. (http://www.boxever.com/). This product includes software developed at SoundCloud Ltd. (http://soundcloud.com/). This product includes software developed as part of the Ocelli project by Netflix Inc. (https://github.com/Netflix/ocelli/). ================================================ FILE: distribution/src/release/licenses/NOTICE-protostuff ================================================ ============================================================== protostuff Copyright 2009 David Yu dyuproject@gmail.com ============================================================== protobuf is copyright Google inc unless otherwise noted. It is licensed under the BSD license. jackson-core-asl is copyright FasterXml unless otherwise noted. It is licensed under the apache 2.0 license. antlr is copyright Terence Parr unless otherwise noted. It is licensed under the BSD license. stringtemplate is copyright Terence Parr unless otherwise noted. It is licensed under the BSD license. velocity is licensed under the apache 2.0 license. B64Code.java is copyright Mort Bay Consulting Pty Ltd unless otherwise noted. It is licensed under the apache 2.0 license. jarjar is copyright Google inc unless otherwise noted. It is licensed under the apache 2.0 license. guava is copyright Google inc unless otherwise noted. It is licensed under the apache 2.0 license. ================================================ FILE: distribution/src/release/licenses/NOTICE-tomcat ================================================ Apache Tomcat Copyright 1999-2018 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). This software contains code derived from netty-native developed by the Netty project (http://netty.io, https://github.com/netty/netty-tcnative/) and from finagle-native developed at Twitter (https://github.com/twitter/finagle). The Windows Installer is built with the Nullsoft Scriptable Install System (NSIS), which is open source software. The original software and related information is available at http://nsis.sourceforge.net. Java compilation software for JSP pages is provided by the Eclipse JDT Core Batch Compiler component, which is open source software. The original software and related information is available at http://www.eclipse.org/jdt/core/. For portions of the Tomcat JNI OpenSSL API and the OpenSSL JSSE integration The org.apache.tomcat.jni and the org.apache.tomcat.net.openssl packages are derivative work originating from the Netty project and the finagle-native project developed at Twitter * Copyright 2014 The Netty Project * Copyright 2014 Twitter The original XML Schemas for Java EE Deployment Descriptors: - javaee_5.xsd - javaee_web_services_1_2.xsd - javaee_web_services_client_1_2.xsd - javaee_6.xsd - javaee_web_services_1_3.xsd - javaee_web_services_client_1_3.xsd - jsp_2_2.xsd - web-app_3_0.xsd - web-common_3_0.xsd - web-fragment_3_0.xsd - javaee_7.xsd - javaee_web_services_1_4.xsd - javaee_web_services_client_1_4.xsd - jsp_2_3.xsd - web-app_3_1.xsd - web-common_3_1.xsd - web-fragment_3_1.xsd - javaee_8.xsd - web-app_4_0.xsd - web-common_4_0.xsd - web-fragment_4_0.xsd may be obtained from: http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/index.html ================================================ FILE: dynamic-config/config-apollo/pom.xml ================================================ dynamic-config org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-apollo Java Chassis::Dynamic Config::Apollo org.apache.servicecomb foundation-config org.apache.servicecomb foundation-vertx org.springframework spring-web ================================================ FILE: dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.apollo; import static org.apache.servicecomb.config.apollo.ConfigurationAction.CREATE; import static org.apache.servicecomb.config.apollo.ConfigurationAction.DELETE; import static org.apache.servicecomb.config.apollo.ConfigurationAction.SET; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.config.apollo.ApolloDynamicPropertiesSource.UpdateHandler; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.annotations.VisibleForTesting; import io.netty.handler.codec.http.HttpResponseStatus; public class ApolloClient { private static final Logger LOGGER = LoggerFactory.getLogger(ApolloClient.class); private static final Map originalConfigMap = new ConcurrentHashMap<>(); private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1); private final ApolloConfig apolloConfig; private final UpdateHandler updateHandler; private static RestTemplate rest = new RestTemplate(); public ApolloClient(UpdateHandler updateHandler, ApolloConfig apolloConfig) { this.updateHandler = updateHandler; this.apolloConfig = apolloConfig; } @VisibleForTesting static Map getOriginalConfigMap() { return originalConfigMap; } @VisibleForTesting static void setRest(RestTemplate rest) { ApolloClient.rest = rest; } public void refreshApolloConfig() { EXECUTOR.scheduleWithFixedDelay(new ConfigRefresh(apolloConfig.getServerUri()), apolloConfig.getFirstRefreshInterval(), apolloConfig.getRefreshInterval(), TimeUnit.SECONDS); } class ConfigRefresh implements Runnable { private final String serviceUri; ConfigRefresh(String serviceUris) { this.serviceUri = serviceUris; } @Override public void run() { try { refreshConfig(); } catch (Throwable e) { LOGGER.error("client refresh thread exception ", e); } } @SuppressWarnings("unchecked") void refreshConfig() { HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json;charset=UTF-8"); headers.add("Authorization", apolloConfig.getToken()); HttpEntity entity = new HttpEntity<>(headers); ResponseEntity exchange = rest.exchange(composeAPI(), HttpMethod.GET, entity, String.class); if (HttpResponseStatus.OK.code() == exchange.getStatusCode().value()) { try { Map body = JsonUtils.OBJ_MAPPER.readValue(exchange.getBody(), new TypeReference>() { }); refreshConfigItems((Map) body.get("configurations")); } catch (IOException e) { LOGGER.error("JsonObject parse config center response error: ", e); } } else { LOGGER.error("fetch configuration failed, error code:{} for {}", exchange.getStatusCode().value(), exchange.getBody()); } } private String composeAPI() { String api = serviceUri + "/openapi/v1/envs/" + apolloConfig.getEnv() + "/apps/" + apolloConfig.getServiceName() + "/clusters/" + apolloConfig.getServerClusters() + "/namespaces/" + apolloConfig.getNamespace() + "/releases/latest"; return api; } private void refreshConfigItems(Map map) { compareChangedConfig(originalConfigMap, map); originalConfigMap.clear(); originalConfigMap.putAll(map); } void compareChangedConfig(Map before, Map after) { Map itemsCreated = new HashMap<>(); Map itemsDeleted = new HashMap<>(); Map itemsModified = new HashMap<>(); if (before == null || before.isEmpty()) { updateHandler.handle(CREATE, after); return; } if (after == null || after.isEmpty()) { updateHandler.handle(DELETE, before); return; } after.forEach((itemKey, itemValue) -> { if (!before.containsKey(itemKey)) { itemsCreated.put(itemKey, itemValue); } else if (!itemValue.equals(before.get(itemKey))) { itemsModified.put(itemKey, itemValue); } }); for (String itemKey : before.keySet()) { if (!after.containsKey(itemKey)) { itemsDeleted.put(itemKey, ""); } } updateHandler.handle(CREATE, itemsCreated); updateHandler.handle(SET, itemsModified); updateHandler.handle(DELETE, itemsDeleted); } } } ================================================ FILE: dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.apollo; import org.springframework.core.env.Environment; public class ApolloConfig { private static final String SERVER_URL_KEY = "apollo.config.serverUri"; private static final String SERVER_NAMESPACE = "apollo.config.namespace"; private static final String SERVER_ENV = "apollo.config.env"; private static final String SERVER_CLUSTERS = "apollo.config.clusters"; private static final String APOLLO_SERVICE_NAME = "apollo.config.serviceName"; private static final String TOKEN = "apollo.config.token"; private static final String REFRESH_INTERVAL = "apollo.config.refreshInterval"; private static final String FIRST_REFRESH_INTERVAL = "apollo.config.firstRefreshInterval"; private static final int DEFAULT_REFRESH_INTERVAL = 3; private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; private final Environment environment; public ApolloConfig(Environment environment) { this.environment = environment; } public String getServiceName() { return environment.getProperty(APOLLO_SERVICE_NAME); } public String getServerUri() { return environment.getProperty(SERVER_URL_KEY); } public String getToken() { return environment.getProperty(TOKEN); } public String getEnv() { return environment.getProperty(SERVER_ENV); } public String getNamespace() { return environment.getProperty(SERVER_NAMESPACE); } public String getServerClusters() { return environment.getProperty(SERVER_CLUSTERS); } public int getRefreshInterval() { return environment.getProperty(REFRESH_INTERVAL, int.class, DEFAULT_REFRESH_INTERVAL); } public int getFirstRefreshInterval() { return environment.getProperty(FIRST_REFRESH_INTERVAL, int.class, DEFAULT_FIRST_REFRESH_INTERVAL); } } ================================================ FILE: dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.apollo; import org.springframework.context.annotation.Configuration; @Configuration public class ApolloConfiguration { } ================================================ FILE: dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.apollo; import static org.apache.servicecomb.config.apollo.ConfigurationAction.CREATE; import static org.apache.servicecomb.config.apollo.ConfigurationAction.DELETE; import static org.apache.servicecomb.config.apollo.ConfigurationAction.SET; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.config.ConfigMapping; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; public class ApolloDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "apollo"; private static final Logger LOGGER = LoggerFactory.getLogger(ApolloDynamicPropertiesSource.class); private final Map valueCache = new ConcurrentHashMap<>(); private final ApolloDynamicPropertiesSource.UpdateHandler updateHandler = new ApolloDynamicPropertiesSource.UpdateHandler(); public ApolloDynamicPropertiesSource() { } @Override public int getOrder() { return 300; } private void init(Environment environment) { ApolloConfig apolloConfig = new ApolloConfig(environment); ApolloClient apolloClient = new ApolloClient(updateHandler, apolloConfig); apolloClient.refreshApolloConfig(); } public class UpdateHandler { public void handle(ConfigurationAction action, Map config) { if (config == null || config.isEmpty()) { return; } Map configuration = ConfigMapping.getConvertedMap(config); if (CREATE.equals(action)) { valueCache.putAll(configuration); } else if (SET.equals(action)) { valueCache.putAll(configuration); } else if (DELETE.equals(action)) { configuration.keySet().forEach(valueCache::remove); } else { LOGGER.error("action: {} is invalid.", action.name()); return; } LOGGER.warn("Config value cache changed: action:{}; item:{}", action.name(), configuration.keySet()); } } @Override public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, valueCache); } } ================================================ FILE: dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ConfigurationAction.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.apollo; public enum ConfigurationAction { CREATE, SET, DELETE } ================================================ FILE: dynamic-config/config-apollo/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.apollo.ApolloDynamicPropertiesSource ================================================ FILE: dynamic-config/config-apollo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.config.apollo.ApolloConfiguration ================================================ FILE: dynamic-config/config-apollo/src/test/java/org/apache/servicecomb/config/apollo/ApolloClientTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.apollo; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.config.apollo.ApolloClient.ConfigRefresh; import org.apache.servicecomb.config.apollo.ApolloDynamicPropertiesSource.UpdateHandler; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; public class ApolloClientTest { @BeforeAll public static void setUpClass() { } @Test public void refreshApolloConfig() { ApolloConfig apolloConfig = Mockito.mock(ApolloConfig.class); RestTemplate rest = Mockito.mock(RestTemplate.class); ApolloClient.setRest(rest); ResponseEntity responseEntity = new ResponseEntity<>( "{\"apollo\":\"mocked\", \"configurations\":{\"timeout\":1000}}", HttpStatus.OK); Mockito.when(rest.exchange( ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.>any(), ArgumentMatchers.>any())).thenReturn(responseEntity); ApolloDynamicPropertiesSource impl = new ApolloDynamicPropertiesSource(); UpdateHandler updateHandler = impl.new UpdateHandler(); ApolloClient apolloClient = new ApolloClient(updateHandler, apolloConfig); ConfigRefresh cr = apolloClient.new ConfigRefresh(apolloConfig.getServerUri()); cr.run(); Assertions.assertEquals(1, ApolloClient.getOriginalConfigMap().size()); } @Test public void testCompareChangedConfig() { ApolloConfig apolloConfig = Mockito.mock(ApolloConfig.class); boolean status = true; Map before = new HashMap<>(); Map after = new HashMap<>(); ApolloDynamicPropertiesSource impl = new ApolloDynamicPropertiesSource(); UpdateHandler updateHandler = impl.new UpdateHandler(); ApolloClient apolloClient = new ApolloClient(updateHandler, apolloConfig); ConfigRefresh cr = apolloClient.new ConfigRefresh(""); try { cr.compareChangedConfig(before, after); } catch (Exception e) { status = false; } Assertions.assertTrue(status); before.put("test", "testValue"); try { cr.compareChangedConfig(before, after); } catch (Exception e) { status = false; } Assertions.assertTrue(status); after.put("test", "testValue2"); try { cr.compareChangedConfig(before, after); } catch (Exception e) { status = false; } Assertions.assertTrue(status); try { cr.compareChangedConfig(before, after); } catch (Exception e) { status = false; } Assertions.assertTrue(status); } } ================================================ FILE: dynamic-config/config-apollo/src/test/resources/microservice.yaml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- APPLICATION_ID: apollotest service_description: name: apollo-test version: 1.0.1 apollo: config: serverUri: http://127.0.0.1:8070 serviceName: apollo-test env: DEV clusters: test-cluster namespace: application token: xxx refreshInterval: 30 firstRefreshInterval: 0 servicecomb: service: registry: address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 highway: address: 0.0.0.0:7070 ================================================ FILE: dynamic-config/config-cc/pom.xml ================================================ dynamic-config org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-cc Java Chassis::Foundations::Config CC org.apache.servicecomb foundation-config org.apache.servicecomb foundation-ssl org.apache.servicecomb foundation-vertx io.vertx vertx-codegen provided org.apache.servicecomb java-chassis-core org.apache.servicecomb foundation-test-scaffolding test org.apache.servicecomb config-center-client ================================================ FILE: dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import java.util.Collections; import java.util.List; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.foundation.vertx.VertxConst; import org.springframework.core.env.Environment; public final class ConfigCenterConfig { public static final String SSL_TAG = "cc.consumer"; private static final String ADDRESS = "servicecomb.config.client.serverUri"; private static final String DOMAIN_NAME = "servicecomb.config.client.domainName"; private static final String REFRESH_INTERVAL = "servicecomb.config.client.refresh_interval"; private static final String FIRST_PULL_REQUIRED = "servicecomb.config.client.firstPullRequired"; public static final String FILE_SOURCE = "servicecomb.config.client.fileSource"; private static final int DEFAULT_REFRESH_INTERVAL = 15000; private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.config.client.timeout.connect"; private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.config.client.timeout.request"; private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.config.client.timeout.socket"; private final Environment environment; public ConfigCenterConfig(Environment environment) { this.environment = environment; } public String getDomainName() { return environment.getProperty(DOMAIN_NAME, "default"); } public boolean firstPullRequired() { return environment.getProperty(FIRST_PULL_REQUIRED, boolean.class, false); } @SuppressWarnings("unchecked") public List getFileSources() { return environment.getProperty(FILE_SOURCE, List.class, Collections.emptyList()); } public long getRefreshInterval() { return environment.getProperty( REFRESH_INTERVAL, long.class, (long) DEFAULT_REFRESH_INTERVAL); } public Boolean isProxyEnable() { return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); } public String getProxyHost() { return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); } public int getProxyPort() { return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); } public String getProxyUsername() { return environment.getProperty(VertxConst.PROXY_USERNAME); } public String getProxyPasswd() { return environment.getProperty(VertxConst.PROXY_PASSWD); } public List getServerUri() { return ConfigUtil.parseArrayValue(environment.getProperty(ADDRESS, "")); } public int getConnectTimeout() { return environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000); } public int getConnectionRequestTimeout() { return environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000); } public int getSocketTimeout() { return environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000); } } ================================================ FILE: dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import org.springframework.context.annotation.Bean; public class ConfigCenterConfiguration { @Bean public ConfigCenterInformationCollector configCenterInformationCollector() { return new ConfigCenterInformationCollector(); } } ================================================ FILE: dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.apache.servicecomb.config.center.client.ConfigCenterAddressManager; import org.apache.servicecomb.config.center.client.ConfigCenterClient; import org.apache.servicecomb.config.center.client.ConfigCenterConfigurationChangedEvent; import org.apache.servicecomb.config.center.client.ConfigCenterManager; import org.apache.servicecomb.config.center.client.model.ConfigCenterConfiguration; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; import org.apache.servicecomb.config.common.ConfigConverter; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import com.google.common.eventbus.Subscribe; public class ConfigCenterDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "config-center"; private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterDynamicPropertiesSource.class); private final Map data = new ConcurrentHashMapEx<>(); private ConfigConverter configConverter; public ConfigCenterDynamicPropertiesSource() { } private void init(Environment environment) { ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(environment); configConverter = new ConfigConverter(configCenterConfig.getFileSources()); ConfigCenterAddressManager configCenterAddressManager = configCenterAddressManager(configCenterConfig, environment); HttpTransport httpTransport = createHttpTransport(configCenterAddressManager, buildRequestConfig(configCenterConfig), environment, configCenterConfig); ConfigCenterClient configCenterClient = new ConfigCenterClient(configCenterAddressManager, httpTransport); EventManager.register(this); ConfigCenterConfiguration configCenterConfiguration = createConfigCenterConfiguration(configCenterConfig); QueryConfigurationsRequest queryConfigurationsRequest = firstPull(configCenterConfig, configCenterClient, environment, configCenterAddressManager); ConfigCenterManager configCenterManager = new ConfigCenterManager(configCenterClient, EventManager.getEventBus(), configConverter, configCenterConfiguration, configCenterAddressManager); configCenterManager.setQueryConfigurationsRequest(queryConfigurationsRequest); configCenterManager.startConfigCenterManager(); data.putAll(configConverter.getCurrentData()); } private RequestConfig buildRequestConfig(ConfigCenterConfig config) { RequestConfig.Builder builder = HttpTransportFactory.defaultRequestConfig(); builder.setConnectTimeout(config.getConnectTimeout()); builder.setConnectionRequestTimeout(config.getConnectionRequestTimeout()); builder.setSocketTimeout(config.getSocketTimeout()); return builder.build(); } private QueryConfigurationsRequest firstPull(ConfigCenterConfig configCenterConfig, ConfigCenterClient configCenterClient, Environment environment, ConfigCenterAddressManager configCenterAddressManager) { QueryConfigurationsRequest queryConfigurationsRequest = createQueryConfigurationsRequest(environment); try { QueryConfigurationsResponse response = configCenterClient .queryConfigurations(queryConfigurationsRequest, configCenterAddressManager.address()); if (response.isChanged()) { configConverter.updateData(response.getConfigurations()); queryConfigurationsRequest.setRevision(response.getRevision()); } } catch (Exception e) { if (configCenterConfig.firstPullRequired()) { throw e; } LOGGER.warn("first pull failed, and ignore {}", e.getMessage()); } return queryConfigurationsRequest; } @Subscribe public void onConfigurationChangedEvent(ConfigCenterConfigurationChangedEvent event) { LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); data.putAll(event.getAdded()); data.putAll(event.getUpdated()); event.getDeleted().forEach((k, v) -> data.remove(k)); EventManager.post(ConfigurationChangedEvent.createIncremental(event.getAdded(), event.getUpdated(), event.getDeleted())); } private QueryConfigurationsRequest createQueryConfigurationsRequest(Environment environment) { QueryConfigurationsRequest request = new QueryConfigurationsRequest(); request.setApplication(BootStrapProperties.readApplication(environment)); request.setServiceName(BootStrapProperties.readServiceName(environment)); request.setVersion(BootStrapProperties.readServiceVersion(environment)); request.setEnvironment(BootStrapProperties.readServiceEnvironment(environment)); // 需要设置为 null, 并且 query 参数为 revision=null 才会返回 revision 信息。 revision = 是不行的。 request.setRevision(null); return request; } private ConfigCenterConfiguration createConfigCenterConfiguration( ConfigCenterConfig configCenterConfig) { return new ConfigCenterConfiguration().setRefreshIntervalInMillis(configCenterConfig.getRefreshInterval()); } private HttpTransport createHttpTransport(ConfigCenterAddressManager configCenterAddressManager, RequestConfig requestConfig, Environment environment, ConfigCenterConfig configCenterConfig) { List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); if (configCenterConfig.isProxyEnable()) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(requestConfig); HttpHost proxy = new HttpHost(configCenterConfig.getProxyHost(), configCenterConfig.getProxyPort(), "http"); // now only support http proxy httpClientBuilder.setProxy(proxy); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(configCenterConfig.getProxyUsername(), configCenterConfig.getProxyPasswd())); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return HttpTransportFactory .createHttpTransport( TransportUtils .createSSLProperties(configCenterAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); } return HttpTransportFactory .createHttpTransport( TransportUtils .createSSLProperties(configCenterAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); } private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } private ConfigCenterAddressManager configCenterAddressManager(ConfigCenterConfig configCenterConfig, Environment environment) { String region = environment.getProperty("servicecomb.datacenter.region"); String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); return new ConfigCenterAddressManager(configCenterConfig.getDomainName(), configCenterConfig.getServerUri(), EventManager.getEventBus(), region, availableZone); } @Override public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, data); } @Override public int getOrder() { return 200; } } ================================================ FILE: dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterInformationCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.lang.NonNull; public class ConfigCenterInformationCollector implements BootUpInformationCollector, EnvironmentAware { private ConfigCenterConfig configCenterConfig; @Override public void setEnvironment(@NonNull Environment environment) { this.configCenterConfig = new ConfigCenterConfig(environment); } @Override public String collect() { return "Config Center: " + configCenterConfig.getServerUri(); } @Override public int getOrder() { return 1; } } ================================================ FILE: dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.springframework.core.env.Environment; public class TransportUtils { public static SSLProperties createSSLProperties(boolean sslEnabled, Environment environment, String tag) { SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(sslEnabled); if (!sslEnabled) { return sslProperties; } SSLOption option = new SSLOption(); option.setEngine(getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine")); option.setProtocols( getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols")); option.setCiphers( getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); option.setAuthPeer( getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); option.setCheckCNHost( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host")); option.setCheckCNWhite( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white")); option.setCheckCNWhiteFile(getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); option.setAllowRenegotiate(getBooleanProperty(environment, DEFAULT_OPTION.isAllowRenegotiate(), "ssl." + tag + ".allowRenegotiate", "ssl.allowRenegotiate")); option.setStorePath( getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath")); option.setClientAuth( getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".clientAuth", "ssl.clientAuth")); option.setTrustStore( getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore")); option.setTrustStoreType(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType")); option.setTrustStoreValue(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue")); option.setKeyStore( getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); option.setKeyStoreType( getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType")); option.setKeyStoreValue(getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue")); option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); option.setSslCustomClass( getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); sslProperties.setSslOption(option); sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); return sslProperties; } private static String getStringProperty(Environment environment, String defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key); } } return defaultValue; } private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key, boolean.class, false); } } return defaultValue; } } ================================================ FILE: dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.cc.ConfigCenterDynamicPropertiesSource ================================================ FILE: dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.cc.ConfigCenterInformationCollector ================================================ FILE: dynamic-config/config-cc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.config.cc.ConfigCenterConfiguration ================================================ FILE: dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.center.client.ConfigCenterAddressManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import com.google.common.eventbus.EventBus; class ConfigCenterAddressManagerTest { private static final List addresses = new ArrayList<>(); private static ConfigCenterAddressManager addressManager1; private static ConfigCenterAddressManager addressManager2; @Test public void addressManagerTest() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); addressManager2 = new ConfigCenterAddressManager(null, addresses, new EventBus(), "", ""); Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); addressManagerField = addressManager2.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager2, 0); Assertions.assertNotNull(addressManager1); Assertions.assertNotNull(addressManager2); List addresses = addressManager1.getAddresses(); Assertions.assertEquals(2, addresses.size()); Assertions.assertEquals("http://127.0.0.1:30103/v3/project", addresses.get(0)); Assertions.assertEquals("https://127.0.0.2:30103/v3/project", addressManager1.address()); Assertions.assertEquals("http://127.0.0.1:30103/v3/project", addressManager1.address()); Assertions.assertEquals("https://127.0.0.2:30103/v3/default", addressManager2.address()); } @Test public void onRefreshEndpointEvent() { List addressAZ = new ArrayList<>(); addressAZ.add("http://127.0.0.3:30100"); List addressRG = new ArrayList<>(); addressRG.add("http://127.0.0.4:30100"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); addressManager1.refreshEndpoint(event, "CseConfigCenter"); List availableZone = addressManager1.getAvailableZone(); Assertions.assertEquals("http://127.0.0.3:30100/v3/project", availableZone.get(0)); List availableRegion = addressManager1.getAvailableRegion(); Assertions.assertEquals("http://127.0.0.4:30100/v3/project", availableRegion.get(0)); Assertions.assertEquals("http://127.0.0.3:30100/v3/project", addressManager1.address()); } } ================================================ FILE: dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.cc; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.center.client.ConfigCenterAddressManager; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ConfigCenterConfigurationSourceImplTest { @Test void configAddressManagerTest() throws IllegalAccessException, NoSuchFieldException { List addresses = new ArrayList<>(); addresses.add("http://127.0.0.1:30103"); addresses.add("http://127.0.0.2:30103"); ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, EventManager.getEventBus(), "", ""); Field addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager, 0); Assertions.assertNotNull(addressManager); String address = addressManager.address(); Assertions.assertEquals("http://127.0.0.2:30103/v3/test", address); address = addressManager.address(); Assertions.assertEquals("http://127.0.0.1:30103/v3/test", address); addressManager = new ConfigCenterAddressManager(null, addresses, EventManager.getEventBus(), "", ""); addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager, 0); address = addressManager.address(); Assertions.assertEquals("http://127.0.0.2:30103/v3/default", address); } @Test void onRefreshEndpointEventTest() { List addresses = new ArrayList<>(); addresses.add("http://127.0.0.1:30103"); List addressAZ = new ArrayList<>(); addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true"); Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", new ArrayList<>()); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, EventManager.getEventBus(), "", ""); addressManager.onRefreshEndpointEvent(event); List availableAZ = addressManager.getAvailableZone(); Assertions.assertEquals("https://127.0.0.1:30100/v3/test", availableAZ.get(0)); } } ================================================ FILE: dynamic-config/config-cc/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- host.name: 172.16.8.7 trace: handler: enabled: false sampler: percent: 0.5 metric: service: enable: false validate: parameter: enabled: true returnValue: enabled: true apiInvoke: enabled: true shutDownHandler: enabled: true timeLimit: 30000 eureka: instance: preferIpAddress: true leaseRenewalIntervalInSeconds: 3 leaseExpirationDurationInSeconds: 5 client: serviceUrl: defaultZone: http://172.16.8.8:30100/ servicecomb: config: client: serviceName: testDemo serverUri: https://172.16.8.7:30103,https://${host.name}:30103 tenantName: csetest refreshMode: 1 refresh_interval: 10000 service: registry: autodiscovery: true service_description: name: testDemo environment: testing ================================================ FILE: dynamic-config/config-consul/pom.xml ================================================ 4.0.0 org.apache.servicecomb dynamic-config 3.4.0-SNAPSHOT config-consul Java Chassis::Dynamic Config::Consul org.apache.servicecomb foundation-common org.apache.servicecomb foundation-registry org.apache.servicecomb java-chassis-core org.kiwiproject consul-client com.google.code.gson gson ================================================ FILE: dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.consul; import org.springframework.core.env.Environment; public class ConsulConfig { public static final String CONSUL_CONFIG_PREFIX = "servicecomb.config.consul"; public static final String CONSUL_DEFAULT_ENVIRONMENT = "production"; public static final String PROPERTY_CONSUL_HOST = "servicecomb.config.consul.host"; public static final String PROPERTY_CONSUL_PORT = "servicecomb.config.consul.port"; public static final String PROPERTY_CONSUL_SCHEME = "servicecomb.config.consul.scheme"; public static final String PROPERTY_CONSUL_ACL_TOKEN = "servicecomb.config.consul.acl-token"; public static final String PROPERTY_CONSUL_WATCH_SECONDS = "servicecomb.config.consul.watch-seconds"; public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.consul.instance-tag"; public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; private final Environment environment; public ConsulConfig(Environment environment) { this.environment = environment; } public String getConsulHost() { return environment.getProperty(PROPERTY_CONSUL_HOST, String.class, "127.0.0.1"); } public int getConsulPort() { return environment.getProperty(PROPERTY_CONSUL_PORT, int.class, 8500); } public String getConsulScheme() { return environment.getProperty(PROPERTY_CONSUL_SCHEME); } public String getConsulAclToken() { return environment.getProperty(PROPERTY_CONSUL_ACL_TOKEN); } public int getConsulWatchSeconds() { return environment.getProperty(PROPERTY_CONSUL_WATCH_SECONDS, int.class, 8); } public String getInstanceTag() { return environment.getProperty(PROPERTY_INSTANCE_TAG); } } ================================================ FILE: dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.consul; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.jetbrains.annotations.NotNull; import org.kiwiproject.consul.Consul; import org.kiwiproject.consul.KeyValueClient; import org.kiwiproject.consul.cache.KVCache; import org.kiwiproject.consul.model.kv.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.env.Environment; import org.springframework.core.io.ByteArrayResource; import java.io.IOException; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Properties; import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_APPLICATION; import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_ENVIRONMENT; import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_SERVICE; import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_TAG; import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_VERSION; public class ConsulConfigClient { private static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigClient.class); public class GetDataRunnable implements Runnable { private Map dataMap; private String path; public GetDataRunnable(String path, Map dataMap) { this.dataMap = dataMap; this.path = path; } @Override public void run() { try { if (path.equals("tagData")) { tagData = dataMap; } else if (path.equals("versionData")) { versionData = dataMap; } else if (path.equals("serviceData")) { serviceData = dataMap; } else if (path.equals("applicationData")) { applicationData = dataMap; } else { environmentData = dataMap; } refreshConfigItems(); } catch (Exception e) { throw new RuntimeException(e); } } } private ConsulDynamicPropertiesSource.UpdateHandler updateHandler; private ConsulConfig consulConfig; private Environment environment; private final Object lock = new Object(); private Consul consulClient; private KeyValueClient kvClient; private ConsulConfigProperties consulConfigProperties; private Map environmentData = new HashMap<>(); private Map applicationData = new HashMap<>(); private Map serviceData = new HashMap<>(); private Map versionData = new HashMap<>(); private Map tagData = new HashMap<>(); private Map allLast = new HashMap<>(); public ConsulConfigClient(ConsulDynamicPropertiesSource.UpdateHandler updateHandler, Environment environment, ConsulConfigProperties consulConfigProperties, Consul consulClient) { this.updateHandler = updateHandler; this.consulConfig = new ConsulConfig(environment); this.environment = environment; this.consulConfigProperties = consulConfigProperties; this.consulClient = consulClient; this.kvClient = consulClient.keyValueClient(); } public void refreshConsulConfig() { String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = ConsulConfig.CONSUL_DEFAULT_ENVIRONMENT; } addEnvironmentConfig(env); addApplicationConfig(env); addServiceConfig(env); addVersionConfig(env); addTagConfig(env); refreshConfigItems(); } private void addTagConfig(String env) { if (StringUtils.isBlank(consulConfig.getInstanceTag())) { return; } String path = String.format(PATH_TAG, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment), consulConfig.getInstanceTag()); this.tagData = parseData(path); try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { cache.addListener(newValues -> { Optional newValue = newValues.values().stream() .filter(value -> value.getKey().equals(path)) .findAny(); newValue.ifPresent(value -> { Optional decodedValue = newValue.get().getValueAsString(); decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("tagData", getValues(path))).start()); }); }); cache.start(); } } private void addVersionConfig(String env) { String path = String.format(PATH_VERSION, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment)); this.versionData = parseData(path); try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { cache.addListener(newValues -> { Optional newValue = newValues.values().stream() .filter(value -> value.getKey().equals(path)) .findAny(); newValue.ifPresent(value -> { Optional decodedValue = newValue.get().getValueAsString(); decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("versionData", getValues(path))).start()); }); }); cache.start(); } } private void addServiceConfig(String env) { String path = String.format(PATH_SERVICE, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment)); this.serviceData = parseData(path); try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { cache.addListener(newValues -> { Optional newValue = newValues.values().stream() .filter(value -> value.getKey().equals(path)) .findAny(); newValue.ifPresent(value -> { Optional decodedValue = newValue.get().getValueAsString(); decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("serviceData", getValues(path))).start()); }); }); cache.start(); } } private void addApplicationConfig(String env) { String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); this.applicationData = parseData(path); try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { cache.addListener(newValues -> { Optional newValue = newValues.values().stream() .filter(value -> value.getKey().equals(path)) .findAny(); newValue.ifPresent(value -> { Optional decodedValue = newValue.get().getValueAsString(); decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("applicationData", getValues(path))).start()); }); }); cache.start(); } } private void addEnvironmentConfig(String env) { String path = String.format(PATH_ENVIRONMENT, env); this.environmentData = parseData(path); try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { cache.addListener(newValues -> { Optional newValue = newValues.values().stream() .filter(value -> value.getKey().equals(path)) .findAny(); newValue.ifPresent(value -> { Optional decodedValue = newValue.get().getValueAsString(); decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("environmentData", getValues(path))).start()); }); }); cache.start(); } } public Map parseData(String path) { try { return getValues(path); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } return new HashMap<>(); } private Map getValues(String path) { Map values = new HashMap<>(); KeyValueClient keyValueClient = consulClient.keyValueClient(); String decodedValue = keyValueClient.getValueAsString(path).orElseThrow(); if (StringUtils.isBlank(decodedValue)) { return values; } return getValues(path, decodedValue); } private @NotNull Map getValues(String path, String decodedValue) { Map values = new HashMap<>(); if (path.endsWith(".yaml") || path.endsWith(".yml")) { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(decodedValue.getBytes(StandardCharsets.UTF_8))); values.putAll(toMap(yamlFactory.getObject())); } else if (path.endsWith(".properties")) { Properties properties = new Properties(); try { properties.load(new StringReader(decodedValue)); } catch (IOException e) { LOGGER.error(e.getMessage(), e); } values.putAll(toMap(properties)); } else { values.put(path, decodedValue); } return values; } private void refreshConfigItems() { synchronized (lock) { Map all = new HashMap<>(); all.putAll(environmentData); all.putAll(applicationData); all.putAll(serviceData); all.putAll(versionData); all.putAll(tagData); updateHandler.handle(all, allLast); this.allLast = all; } } @SuppressWarnings("unchecked") private Map toMap(Properties properties) { if (properties == null) { return Collections.emptyMap(); } Map result = new HashMap<>(); Enumeration keys = (Enumeration) properties.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Object value = properties.getProperty(key); result.put(key, value); } return result; } } ================================================ FILE: dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.consul; public class ConsulConfigProperties { private String host = "localhost"; private Integer port = 8500; private String scheme = "http"; private String aclToken; private Integer watchSeconds = 8; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public String getScheme() { return scheme; } public void setScheme(String scheme) { this.scheme = scheme; } public String getAclToken() { return aclToken; } public void setAclToken(String aclToken) { this.aclToken = aclToken; } public Integer getWatchSeconds() { return watchSeconds; } public void setWatchSeconds(Integer watchSeconds) { this.watchSeconds = watchSeconds; } } ================================================ FILE: dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.consul; import com.google.common.net.HostAndPort; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.apache.servicecomb.foundation.common.event.EventManager; import org.kiwiproject.consul.Consul; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ConsulDynamicPropertiesSource implements DynamicPropertiesSource { private static final Logger LOGGER = LoggerFactory.getLogger(ConsulDynamicPropertiesSource.class); public static final String SOURCE_NAME = "consul"; private final Map valueCache = new ConcurrentHashMap<>(); private ConsulConfigClient consulConfigClient; public ConsulDynamicPropertiesSource() { } private final UpdateHandler updateHandler = new UpdateHandler(); public class UpdateHandler { public void handle(Map current, Map last) { ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); valueCache.putAll(event.getAdded()); valueCache.putAll(event.getUpdated()); event.getDeleted().forEach((k, v) -> valueCache.remove(k)); EventManager.post(event); } } private ConsulConfigProperties consulConfigProperties(Environment environment) { ConsulConfig consulConfig = new ConsulConfig(environment); ConsulConfigProperties consulConfigProperties = new ConsulConfigProperties(); consulConfigProperties.setHost(consulConfig.getConsulHost()); consulConfigProperties.setPort(consulConfig.getConsulPort()); consulConfigProperties.setScheme(consulConfig.getConsulScheme()); consulConfigProperties.setAclToken(consulConfig.getConsulAclToken()); consulConfigProperties.setWatchSeconds(consulConfig.getConsulWatchSeconds()); return consulConfigProperties; } private Consul consulClient(ConsulConfigProperties consulConfigProperties) { Consul.Builder builder = Consul.builder().withHostAndPort(HostAndPort.fromParts(consulConfigProperties.getHost(), consulConfigProperties.getPort())); if (StringUtils.isNotBlank(consulConfigProperties.getAclToken())) { builder.withAclToken(consulConfigProperties.getAclToken()); } return builder.build(); } private ConsulConfigClient consulConfigClient(Environment environment) { ConsulConfigProperties consulConfigProperties = consulConfigProperties(environment); Consul consulClient = consulClient(consulConfigProperties); return new ConsulConfigClient(updateHandler, environment, consulConfigProperties, consulClient); } @Override public PropertySource create(Environment environment) { try { consulConfigClient = consulConfigClient(environment); consulConfigClient.refreshConsulConfig(); } catch (Exception e) { throw new IllegalStateException("Set up consul config failed.", e); } return new MapPropertySource(SOURCE_NAME, valueCache); } @Override public int getOrder() { return 0; } } ================================================ FILE: dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.consul.ConsulDynamicPropertiesSource ================================================ FILE: dynamic-config/config-etcd/pom.xml ================================================ dynamic-config org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-etcd Java Chassis::Dynamic Config::Zookeeper org.apache.servicecomb foundation-config org.apache.servicecomb foundation-vertx io.etcd jetcd-core ================================================ FILE: dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.etcd; import java.io.IOException; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.etcd.EtcdDynamicPropertiesSource.UpdateHandler; import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.env.Environment; import org.springframework.core.io.ByteArrayResource; import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; import io.etcd.jetcd.KeyValue; import io.etcd.jetcd.Watch; import io.etcd.jetcd.kv.GetResponse; import io.etcd.jetcd.options.GetOption; import io.etcd.jetcd.options.WatchOption; public class EtcdClient { public class GetDataRunnable implements Runnable { private Map dataMap; private EtcdClient etcdClient; private String path; public GetDataRunnable(Map dataMap, EtcdClient etcdClient, String path) { this.dataMap = dataMap; this.etcdClient = etcdClient; this.path = path; } @Override public void run() { try { dataMap.clear(); dataMap.putAll(etcdClient.parseData(path)); refreshConfigItems(); } catch (Exception e) { throw new RuntimeException(e); } } } private static final Logger LOGGER = LoggerFactory.getLogger(EtcdClient.class); public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; private final UpdateHandler updateHandler; private final EtcdConfig etcdConfig; private final Environment environment; private final Object lock = new Object(); private Map environmentData = new HashMap<>(); private Map applicationData = new HashMap<>(); private Map serviceData = new HashMap<>(); private Map versionData = new HashMap<>(); private Map tagData = new HashMap<>(); private Map allLast = new HashMap<>(); private Client client; public EtcdClient(UpdateHandler updateHandler, Environment environment) { this.updateHandler = updateHandler; this.etcdConfig = new EtcdConfig(environment); this.environment = environment; } public void getClient() { if (StringUtils.isEmpty(etcdConfig.getAuthInfo())) { this.client = Client.builder().endpoints(etcdConfig.getConnectString()).build(); } else { String[] authInfo = etcdConfig.getAuthInfo().split(":"); this.client = Client.builder().endpoints(etcdConfig.getConnectString()) .user(ByteSequence.from(authInfo[0], StandardCharsets.UTF_8)) .password(ByteSequence.from(authInfo[1], StandardCharsets.UTF_8)).build(); } } public void refreshEtcdConfig() throws Exception { getClient(); String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = EtcdConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; } addEnvironmentConfig(env); addApplicationConfig(env); addServiceConfig(env); addVersionConfig(env); addTagConfig(env); refreshConfigItems(); } private void addTagConfig(String env) throws Exception { if (StringUtils.isEmpty(etcdConfig.getInstanceTag())) { return; } String path = String.format(PATH_TAG, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment), etcdConfig.getInstanceTag()); ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunnable(tagData, this, path)).start()); this.tagData = parseData(path); } private void addVersionConfig(String env) throws Exception { String path = String.format(PATH_VERSION, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment)); ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunnable(versionData, this, path)).start()); this.versionData = parseData(path); } private void addServiceConfig(String env) throws Exception { String path = String.format(PATH_SERVICE, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment)); ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunnable(serviceData, this, path)).start()); this.serviceData = parseData(path); } private void addApplicationConfig(String env) throws Exception { String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunnable(applicationData, this, path)).start()); this.applicationData = parseData(path); } private void addEnvironmentConfig(String env) throws Exception { String path = String.format(PATH_ENVIRONMENT, env); ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunnable(environmentData, this, path)).start()); this.environmentData = parseData(path); } public Map parseData(String path) throws Exception { List endpointKv = getValuesByPrefix(path); return getValues(path, endpointKv); } private Map getValues(String path, List endpointKv) { Map values = new HashMap<>(); for (KeyValue keyValue : endpointKv) { String key = new String(keyValue.getKey().getBytes(), StandardCharsets.UTF_8); String value = new String(keyValue.getValue().getBytes(), StandardCharsets.UTF_8); if (key.equals(path)) { continue; } if (key.endsWith(".yaml") || key.endsWith(".yml")) { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(value.getBytes(StandardCharsets.UTF_8))); values.putAll(toMap(yamlFactory.getObject())); } else if (key.endsWith(".properties")) { Properties properties = new Properties(); try { properties.load(new StringReader(value)); } catch (IOException e) { LOGGER.error("load error"); } values.putAll(toMap(properties)); } else { values.put(key, value); } } return values; } private List getValuesByPrefix(String prefix) { CompletableFuture getFuture = client.getKVClient() .get(ByteSequence.from(prefix, StandardCharsets.UTF_8), GetOption.builder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build()); GetResponse response = MuteExceptionUtil.builder().withLog("get kv by prefix error") .executeCompletableFuture(getFuture); return response.getKvs(); } private void refreshConfigItems() { synchronized (lock) { Map all = new HashMap<>(); all.putAll(environmentData); all.putAll(applicationData); all.putAll(serviceData); all.putAll(versionData); all.putAll(tagData); updateHandler.handle(all, allLast); this.allLast = all; } } @SuppressWarnings("unchecked") private Map toMap(Properties properties) { if (properties == null) { return Collections.emptyMap(); } Map result = new HashMap<>(); Enumeration keys = (Enumeration) properties.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Object value = properties.getProperty(key); result.put(key, value); } return result; } } ================================================ FILE: dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.etcd; import org.springframework.core.env.Environment; public class EtcdConfig { public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.etcd.connectString"; public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.etcd.sessionTimeoutMillis"; public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.etcd.connectionTimeoutMills"; public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.etcd.authenticationSchema"; public static final String PROPERTY_AUTH_INFO = "servicecomb.config.etcd.authenticationInfo"; public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.etcd.instanceTag"; private final Environment environment; public EtcdConfig(Environment environment) { this.environment = environment; } public String getConnectString() { return environment.getProperty(PROPERTY_CONNECT_STRING, "http://127.0.0.1:2379"); } public int getSessionTimeoutMillis() { return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); } public int getConnectionTimeoutMillis() { return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); } public String getAuthSchema() { return environment.getProperty(PROPERTY_AUTH_SCHEMA); } public String getAuthInfo() { return environment.getProperty(PROPERTY_AUTH_INFO); } public String getInstanceTag() { return environment.getProperty(PROPERTY_INSTANCE_TAG); } } ================================================ FILE: dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.etcd; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.apache.servicecomb.foundation.common.event.EventManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; public class EtcdDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "etcd"; private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDynamicPropertiesSource.class); private final Map valueCache = new ConcurrentHashMap<>(); public EtcdDynamicPropertiesSource() { } private final UpdateHandler updateHandler = new UpdateHandler(); private void init(Environment environment) { EtcdClient etcdClient = new EtcdClient(updateHandler, environment); try { etcdClient.refreshEtcdConfig(); } catch (Exception e) { throw new IllegalStateException("Set up etcd config failed.", e); } } public class UpdateHandler { public void handle(Map current, Map last) { ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); valueCache.putAll(event.getAdded()); valueCache.putAll(event.getUpdated()); event.getDeleted().forEach((k, v) -> valueCache.remove(k)); EventManager.post(event); } } @Override public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, valueCache); } @Override public int getOrder() { return 0; } } ================================================ FILE: dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.etcd.EtcdDynamicPropertiesSource ================================================ FILE: dynamic-config/config-kie/pom.xml ================================================ dynamic-config org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-kie Java Chassis::Dynamic Config::Kie org.apache.servicecomb foundation-config org.apache.servicecomb foundation-ssl org.apache.servicecomb foundation-vertx io.vertx vertx-codegen provided org.apache.servicecomb java-chassis-core org.apache.servicecomb foundation-test-scaffolding test org.apache.servicecomb config-kie-client ================================================ FILE: dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie; import java.util.Collections; import java.util.List; import org.apache.servicecomb.foundation.vertx.VertxConst; import org.springframework.core.env.Environment; public class KieConfig { public static final String SSL_TAG = "kie.consumer"; private static final String SERVER_URL_KEY = "servicecomb.kie.serverUri"; private static final String REFRESH_INTERVAL = "servicecomb.kie.refreshInterval"; private static final String FIRST_REFRESH_INTERVAL = "servicecomb.kie.firstRefreshInterval"; private static final String DOMAIN_NAME = "servicecomb.kie.domainName"; private static final String ENABLE_LONG_POLLING = "servicecomb.kie.enableLongPolling"; private static final String POLLING_WAIT_TIME = "servicecomb.kie.pollingWaitTime"; private static final String FIRST_PULL_REQUIRED = "servicecomb.kie.firstPullRequired"; private static final String CUSTOM_LABEL = "servicecomb.kie.customLabel"; private static final String CUSTOM_LABEL_VALUE = "servicecomb.kie.customLabelValue"; private static final String ENABLE_APP_CONFIG = "servicecomb.kie.enableAppConfig"; private static final String ENABLE_SERVICE_CONFIG = "servicecomb.kie.enableServiceConfig"; private static final String ENABLE_VERSION_CONFIG = "servicecomb.kie.enableVersionConfig"; private static final String ENABLE_CUSTOM_CONFIG = "servicecomb.kie.enableCustomConfig"; public static final String FILE_SOURCE = "servicecomb.config.client.fileSource"; private static final int DEFAULT_REFRESH_INTERVAL = 15000; private static final int DEFAULT_POLLING_WAIT_TIME = 10; private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; private static final boolean DEFAULT_ENABLE_LONG_POLLING = true; private static final String CUSTOM_LABEL_DEFAULT = "public"; private static final String CUSTOM_LABEL_VALUE_DEFAULT = ""; private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.kie.client.timeout.connect"; private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.kie.client.timeout.request"; private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.kie.client.timeout.socket"; private final Environment environment; public KieConfig(Environment environment) { this.environment = environment; } @SuppressWarnings("unchecked") public List getFileSources() { return environment.getProperty(FILE_SOURCE, List.class, Collections.emptyList()); } public String getDomainName() { return environment.getProperty(DOMAIN_NAME, "default"); } public String getServerUri() { return environment.getProperty(SERVER_URL_KEY); } public int getRefreshInterval() { return environment.getProperty(REFRESH_INTERVAL, int.class, DEFAULT_REFRESH_INTERVAL); } public int getFirstRefreshInterval() { return environment.getProperty(FIRST_REFRESH_INTERVAL, int.class, DEFAULT_FIRST_REFRESH_INTERVAL); } public boolean enableAppConfig() { return environment.getProperty(ENABLE_APP_CONFIG, boolean.class, true); } public boolean enableServiceConfig() { return environment.getProperty(ENABLE_SERVICE_CONFIG, boolean.class, true); } public boolean enableVersionConfig() { return environment.getProperty(ENABLE_VERSION_CONFIG, boolean.class, true); } public boolean enableCustomConfig() { return environment.getProperty(ENABLE_CUSTOM_CONFIG, boolean.class, true); } public boolean enableLongPolling() { return environment.getProperty(ENABLE_LONG_POLLING, boolean.class, DEFAULT_ENABLE_LONG_POLLING); } public int getPollingWaitTime() { return environment.getProperty(POLLING_WAIT_TIME, int.class, DEFAULT_POLLING_WAIT_TIME); } public boolean firstPullRequired() { return environment.getProperty(FIRST_PULL_REQUIRED, boolean.class, false); } public String getCustomLabel() { return environment.getProperty(CUSTOM_LABEL, CUSTOM_LABEL_DEFAULT); } public String getCustomLabelValue() { return environment.getProperty(CUSTOM_LABEL_VALUE, CUSTOM_LABEL_VALUE_DEFAULT); } public Boolean isProxyEnable() { return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); } public String getProxyHost() { return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); } public int getProxyPort() { return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); } public String getProxyUsername() { return environment.getProperty(VertxConst.PROXY_USERNAME); } public String getProxyPasswd() { return environment.getProperty(VertxConst.PROXY_PASSWD); } public int getConnectTimeout() { return environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000); } public int getConnectionRequestTimeout() { return environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000); } public int getSocketTimeout() { return environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000); } } ================================================ FILE: dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfigConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie; import org.apache.servicecomb.config.kie.collect.KieClientInformationCollector; import org.springframework.context.annotation.Bean; public class KieConfigConfiguration { @Bean public KieClientInformationCollector kieClientInformationCollector() { return new KieClientInformationCollector(); } } ================================================ FILE: dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.apache.servicecomb.config.common.ConfigConverter; import org.apache.servicecomb.config.kie.client.KieClient; import org.apache.servicecomb.config.kie.client.KieConfigManager; import org.apache.servicecomb.config.kie.client.KieConfigurationChangedEvent; import org.apache.servicecomb.config.kie.client.model.KieAddressManager; import org.apache.servicecomb.config.kie.client.model.KieConfiguration; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import com.google.common.eventbus.Subscribe; public class KieDynamicPropertiesSource implements DynamicPropertiesSource { private static final Logger LOGGER = LoggerFactory.getLogger(KieDynamicPropertiesSource.class); public static final String SOURCE_NAME = "kie"; private final Map data = new ConcurrentHashMapEx<>(); private KieConfigManager kieConfigManager; private ConfigConverter configConverter; public KieDynamicPropertiesSource() { } private void init(Environment environment) { KieConfig kieConfig = new KieConfig(environment); configConverter = new ConfigConverter(kieConfig.getFileSources()); KieAddressManager kieAddressManager = configKieAddressManager(kieConfig, environment); RequestConfig.Builder requestBuilder = buildRequestConfigBuilder(kieConfig); if (kieConfig.enableLongPolling() && kieConfig.getPollingWaitTime() >= 0) { requestBuilder.setConnectionRequestTimeout(kieConfig.getPollingWaitTime() * 2 * 1000); requestBuilder.setSocketTimeout(kieConfig.getPollingWaitTime() * 2 * 1000); } HttpTransport httpTransport = createHttpTransport(kieAddressManager, requestBuilder.build(), kieConfig, environment); KieConfiguration kieConfiguration = createKieConfiguration(kieConfig, environment); KieClient kieClient = new KieClient(kieAddressManager, httpTransport, kieConfiguration); EventManager.register(this); kieConfigManager = new KieConfigManager(kieClient, EventManager.getEventBus(), kieConfiguration, configConverter, kieAddressManager); kieConfigManager.firstPull(); kieConfigManager.startConfigKieManager(); data.putAll(configConverter.getCurrentData()); } private Builder buildRequestConfigBuilder(KieConfig kieConfig) { Builder builder = HttpTransportFactory.defaultRequestConfig(); builder.setConnectTimeout(kieConfig.getConnectTimeout()); builder.setConnectionRequestTimeout(kieConfig.getConnectionRequestTimeout()); builder.setSocketTimeout(kieConfig.getSocketTimeout()); return builder; } @Subscribe public void onConfigurationChangedEvent(KieConfigurationChangedEvent event) { LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); data.putAll(event.getAdded()); data.putAll(event.getUpdated()); event.getDeleted().forEach((k, v) -> data.remove(k)); EventManager.post(ConfigurationChangedEvent.createIncremental(event.getAdded(), event.getUpdated(), event.getDeleted())); } private KieConfiguration createKieConfiguration(KieConfig kieConfig, Environment environment) { return new KieConfiguration() .setAppName(BootStrapProperties.readApplication(environment)) .setServiceName(BootStrapProperties.readServiceName(environment)) .setEnvironment(BootStrapProperties.readServiceEnvironment(environment)) .setVersion(BootStrapProperties.readServiceVersion(environment)) .setFirstPullRequired(kieConfig.firstPullRequired()) .setCustomLabel(kieConfig.getCustomLabel()) .setCustomLabelValue(kieConfig.getCustomLabelValue()) .setEnableAppConfig(kieConfig.enableAppConfig()) .setEnableCustomConfig(kieConfig.enableCustomConfig()) .setEnableLongPolling(kieConfig.enableLongPolling()) .setEnableServiceConfig(kieConfig.enableServiceConfig()) .setEnableVersionConfig(kieConfig.enableVersionConfig()) .setPollingWaitInSeconds(kieConfig.getPollingWaitTime()) .setProject(kieConfig.getDomainName()) .setRefreshIntervalInMillis(kieConfig.getRefreshInterval()); } private HttpTransport createHttpTransport(KieAddressManager kieAddressManager, RequestConfig requestConfig, KieConfig kieConfig, Environment environment) { List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); if (kieConfig.isProxyEnable()) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(requestConfig); HttpHost proxy = new HttpHost(kieConfig.getProxyHost(), kieConfig.getProxyPort(), "http"); // now only support http proxy httpClientBuilder.setProxy(proxy); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(kieConfig.getProxyUsername(), kieConfig.getProxyPasswd())); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return HttpTransportFactory .createHttpTransport( TransportUtils .createSSLProperties(kieAddressManager.sslEnabled(), environment, KieConfig.SSL_TAG), getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); } return HttpTransportFactory .createHttpTransport( TransportUtils .createSSLProperties(kieAddressManager.sslEnabled(), environment, KieConfig.SSL_TAG), getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); } private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } private KieAddressManager configKieAddressManager(KieConfig kieConfig, Environment environment) { String region = environment.getProperty("servicecomb.datacenter.region"); String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); return new KieAddressManager( Arrays.asList(kieConfig.getServerUri().split(",")), EventManager.getEventBus(), region, availableZone); } @Override public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, data); } @Override public int getOrder() { return 0; } } ================================================ FILE: dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie; import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.springframework.core.env.Environment; public class TransportUtils { public static SSLProperties createSSLProperties(boolean sslEnabled, Environment environment, String tag) { SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(sslEnabled); if (!sslEnabled) { return sslProperties; } SSLOption option = new SSLOption(); option.setEngine(getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine")); option.setProtocols( getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols")); option.setCiphers( getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); option.setAuthPeer( getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); option.setCheckCNHost( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host")); option.setCheckCNWhite( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white")); option.setCheckCNWhiteFile(getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); option.setAllowRenegotiate(getBooleanProperty(environment, DEFAULT_OPTION.isAllowRenegotiate(), "ssl." + tag + ".allowRenegotiate", "ssl.allowRenegotiate")); option.setStorePath( getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath")); option.setClientAuth( getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".clientAuth", "ssl.clientAuth")); option.setTrustStore( getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore")); option.setTrustStoreType(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType")); option.setTrustStoreValue(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue")); option.setKeyStore( getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); option.setKeyStoreType( getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType")); option.setKeyStoreValue(getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue")); option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); option.setSslCustomClass( getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); sslProperties.setSslOption(option); sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); return sslProperties; } private static String getStringProperty(Environment environment, String defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key); } } return defaultValue; } private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key, boolean.class, false); } } return defaultValue; } } ================================================ FILE: dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieClientInformationCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.kie.collect; import org.apache.servicecomb.config.kie.KieConfig; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.lang.NonNull; public class KieClientInformationCollector implements BootUpInformationCollector, EnvironmentAware { private KieConfig kieConfig; @Override public void setEnvironment(@NonNull Environment environment) { this.kieConfig = new KieConfig(environment); } @Override public String collect() { return "Kie Center: " + kieConfig.getServerUri(); } @Override public int getOrder() { return 2; } } ================================================ FILE: dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.kie.KieDynamicPropertiesSource ================================================ FILE: dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.kie.collect.KieClientInformationCollector ================================================ FILE: dynamic-config/config-kie/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.config.kie.KieConfigConfiguration ================================================ FILE: dynamic-config/config-kie/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- host.name: 172.16.8.7 trace: handler: enabled: false sampler: percent: 0.5 metric: service: enable: false validate: parameter: enabled: true returnValue: enabled: true apiInvoke: enabled: true shutDownHandler: enabled: true timeLimit: 30000 eureka: instance: preferIpAddress: true leaseRenewalIntervalInSeconds: 3 leaseExpirationDurationInSeconds: 5 client: serviceUrl: defaultZone: http://172.16.8.8:30100/ servicecomb: kie: serverUri: https://172.16.8.7:30110 service: registry: autodiscovery: true refreshInterval: 3000 firstRefreshInterval: 0 service_description: name: testDemo environment: testing ================================================ FILE: dynamic-config/config-nacos/README.md ================================================ # Dynamic configurations implementation with [Nacos](https://github.com/alibaba/nacos) Read [developers guide](https://docs.servicecomb.io/java-chassis/zh_CN/config/general-config/) for details. ================================================ FILE: dynamic-config/config-nacos/pom.xml ================================================ dynamic-config org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-nacos Java Chassis::Dynamic Config::Nacos org.apache.servicecomb foundation-config org.apache.servicecomb foundation-vertx com.alibaba.nacos nacos-client ================================================ FILE: dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.nacos; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.nacos.NacosDynamicPropertiesSource.UpdateHandler; import org.apache.servicecomb.config.parser.Parser; import org.springframework.core.env.Environment; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; public class NacosClient { private final UpdateHandler updateHandler; private final NacosConfig nacosConfig; private final Environment environment; private final Object lock = new Object(); private Map application = new HashMap<>(); private Map service = new HashMap<>(); private Map version = new HashMap<>(); private Map profile = new HashMap<>(); private Map custom = new HashMap<>(); private Map allLast = new HashMap<>(); public NacosClient(UpdateHandler updateHandler, Environment environment) { this.updateHandler = updateHandler; this.nacosConfig = new NacosConfig(environment); this.environment = environment; } public void refreshNacosConfig() throws NacosException { Properties properties = nacosProperties(environment, nacosConfig); ConfigService configService = NacosFactory.createConfigService(properties); addApplicationConfig(configService); addServiceConfig(configService); addVersionConfig(configService); addProfileConfig(configService); addCustomConfig(configService); refreshConfigItems(); } private void addApplicationConfig(ConfigService configService) throws NacosException { String content = configService.getConfig(BootStrapProperties.readApplication(environment), BootStrapProperties.readApplication(environment), 5000); processApplicationConfig(content); configService.addListener(BootStrapProperties.readApplication(environment), BootStrapProperties.readApplication(environment), new Listener() { @Override public void receiveConfigInfo(String configInfo) { processApplicationConfig(configInfo); refreshConfigItems(); } @Override public Executor getExecutor() { return null; } }); } private void processApplicationConfig(String content) { if (StringUtils.isEmpty(content)) { this.application = new HashMap<>(); return; } Parser contentParser = Parser.findParser("yaml"); this.application = contentParser.parse(content, "", false); } private void addServiceConfig(ConfigService configService) throws NacosException { String content = configService.getConfig(BootStrapProperties.readServiceName(environment), BootStrapProperties.readApplication(environment), 5000); processServiceConfig(content); configService.addListener(BootStrapProperties.readServiceName(environment), BootStrapProperties.readApplication(environment), new Listener() { @Override public void receiveConfigInfo(String configInfo) { processServiceConfig(configInfo); refreshConfigItems(); } @Override public Executor getExecutor() { return null; } }); } private void processServiceConfig(String content) { if (StringUtils.isEmpty(content)) { this.service = new HashMap<>(); return; } Parser contentParser = Parser.findParser("yaml"); this.service = contentParser.parse(content, "", false); } private void addVersionConfig(ConfigService configService) throws NacosException { String content = configService.getConfig( BootStrapProperties.readServiceName(environment) + "-" + BootStrapProperties.readServiceVersion(environment), BootStrapProperties.readApplication(environment), 5000); processVersionConfig(content); configService.addListener(BootStrapProperties.readServiceName(environment) + "-" + BootStrapProperties.readServiceVersion(environment), BootStrapProperties.readApplication(environment), new Listener() { @Override public void receiveConfigInfo(String configInfo) { processVersionConfig(configInfo); refreshConfigItems(); } @Override public Executor getExecutor() { return null; } }); } private void processVersionConfig(String content) { if (StringUtils.isEmpty(content)) { this.version = new HashMap<>(); return; } Parser contentParser = Parser.findParser("yaml"); this.version = contentParser.parse(content, "", false); } private void addProfileConfig(ConfigService configService) throws NacosException { String profile = environment.getProperty("spring.profiles.active"); if (StringUtils.isEmpty(profile)) { return; } String content = configService.getConfig(BootStrapProperties.readServiceName(environment) + "-" + profile, BootStrapProperties.readApplication(environment), 5000); processProfileConfig(content); configService.addListener(BootStrapProperties.readServiceName(environment) + "-" + profile, BootStrapProperties.readApplication(environment), new Listener() { @Override public void receiveConfigInfo(String configInfo) { processProfileConfig(configInfo); refreshConfigItems(); } @Override public Executor getExecutor() { return null; } }); } private void processProfileConfig(String content) { if (StringUtils.isEmpty(content)) { this.profile = new HashMap<>(); return; } Parser contentParser = Parser.findParser("yaml"); this.profile = contentParser.parse(content, "", false); } private void addCustomConfig(ConfigService configService) throws NacosException { if (StringUtils.isEmpty(nacosConfig.getDataId()) || StringUtils.isEmpty(nacosConfig.getGroup())) { return; } String content = configService.getConfig(nacosConfig.getDataId(), nacosConfig.getGroup(), 5000); processCustomConfig(content); configService.addListener(nacosConfig.getDataId(), nacosConfig.getGroup(), new Listener() { @Override public void receiveConfigInfo(String configInfo) { processCustomConfig(configInfo); refreshConfigItems(); } @Override public Executor getExecutor() { return null; } }); } private void processCustomConfig(String content) { if (StringUtils.isEmpty(content)) { this.custom = new HashMap<>(); return; } Parser contentParser = Parser.findParser(nacosConfig.getContentType()); String keyPrefix = nacosConfig.getGroup() + "." + nacosConfig.getDataId(); this.custom = contentParser.parse(content, keyPrefix, nacosConfig.getAddPrefix()); } private void refreshConfigItems() { synchronized (lock) { Map all = new HashMap<>(); all.putAll(application); all.putAll(service); all.putAll(version); all.putAll(profile); all.putAll(custom); updateHandler.handle(all, allLast); this.allLast = all; } } private static Properties nacosProperties(Environment environment, NacosConfig nacosConfig) { Properties properties = new Properties(); properties.put(NacosConfig.PROP_NAMESPACE, BootStrapProperties.readServiceEnvironment(environment)); properties.put(NacosConfig.PROP_ADDRESS, nacosConfig.getServerAddr()); if (nacosConfig.getUsername() != null) { properties.put(NacosConfig.PROP_USERNAME, nacosConfig.getUsername()); } if (nacosConfig.getPassword() != null) { properties.put(NacosConfig.PROP_PASSWORD, nacosConfig.getPassword()); } if (nacosConfig.getAccessKey() != null) { properties.put(NacosConfig.PROP_ACCESS_KEY, nacosConfig.getAccessKey()); } if (nacosConfig.getSecretKey() != null) { properties.put(NacosConfig.PROP_SECRET_KEY, nacosConfig.getSecretKey()); } return properties; } } ================================================ FILE: dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.nacos; import org.springframework.core.env.Environment; public class NacosConfig { public static final String PROPERTY_DATA_ID = "servicecomb.nacos.dataId"; public static final String PROPERTY_SERVER_ADDR = "servicecomb.nacos.serverAddr"; public static final String PROPERTY_GROUP = "servicecomb.nacos.group"; public static final String PROPERTY_ADD_PREFIX = "servicecomb.nacos.addPrefix"; public static final String PROPERTY_CONTENT_TYPE = "servicecomb.nacos.contentType"; public static final String PROPERTY_USERNAME = "servicecomb.nacos.username"; public static final String PROPERTY_PASSWORD = "servicecomb.nacos.password"; public static final String PROPERTY_ACCESS_KEY = "servicecomb.nacos.accessKey"; public static final String PROPERTY_SECRET_KEY = "servicecomb.nacos.secretKey"; public static final String PROP_NAMESPACE = "namespace"; public static final String PROP_ADDRESS = "serverAddr"; public static final String PROP_USERNAME = "username"; public static final String PROP_PASSWORD = "password"; public static final String PROP_ACCESS_KEY = "accessKey"; public static final String PROP_SECRET_KEY = "secretKey"; private final Environment environment; public NacosConfig(Environment environment) { this.environment = environment; } public String getServerAddr() { return environment.getProperty(PROPERTY_SERVER_ADDR, "http://127.0.0.1:8848"); } public String getDataId() { return environment.getProperty(PROPERTY_DATA_ID); } public String getGroup() { return environment.getProperty(PROPERTY_GROUP); } public String getUsername() { return environment.getProperty(PROPERTY_USERNAME); } public String getPassword() { return environment.getProperty(PROPERTY_PASSWORD); } public String getAccessKey() { return environment.getProperty(PROPERTY_ACCESS_KEY); } public String getSecretKey() { return environment.getProperty(PROPERTY_SECRET_KEY); } public String getContentType() { return environment.getProperty(PROPERTY_CONTENT_TYPE, "yaml"); } public boolean getAddPrefix() { return environment.getProperty(PROPERTY_ADD_PREFIX, boolean.class, false); } } ================================================ FILE: dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.nacos; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.apache.servicecomb.foundation.common.event.EventManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; public class NacosDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "nacos"; private static final Logger LOGGER = LoggerFactory.getLogger(NacosDynamicPropertiesSource.class); private final Map valueCache = new ConcurrentHashMap<>(); public NacosDynamicPropertiesSource() { } private final UpdateHandler updateHandler = new UpdateHandler(); private void init(Environment environment) { NacosClient nacosClient = new NacosClient(updateHandler, environment); try { nacosClient.refreshNacosConfig(); } catch (Exception e) { throw new IllegalStateException("Set up nacos config failed.", e); } } public class UpdateHandler { public void handle(Map current, Map last) { ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); valueCache.putAll(event.getAdded()); valueCache.putAll(event.getUpdated()); event.getDeleted().forEach((k, v) -> valueCache.remove(k)); EventManager.post(event); } } @Override public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, valueCache); } @Override public int getOrder() { return 0; } } ================================================ FILE: dynamic-config/config-nacos/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.nacos.NacosDynamicPropertiesSource ================================================ FILE: dynamic-config/config-nacos/src/test/resources/microservice.yaml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- APPLICATION_ID: nacostest service_description: name: nacos-test version: 1.0.1 servicecomb: service: registry: address: http://127.0.0.1:30100 nacos: serverAddr: 127.0.0.1:8848 dataId: example group: DEFAULT_GROUP rest: address: 0.0.0.0:8080 highway: address: 0.0.0.0:7070 ================================================ FILE: dynamic-config/config-zookeeper/pom.xml ================================================ dynamic-config org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 config-zookeeper Java Chassis::Dynamic Config::Zookeeper org.apache.servicecomb foundation-config org.apache.servicecomb foundation-vertx org.apache.curator curator-framework org.apache.curator curator-recipes ================================================ FILE: dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.zookeeper; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.zookeeper.ZookeeperDynamicPropertiesSource.UpdateHandler; import org.apache.zookeeper.server.auth.DigestLoginModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.env.Environment; import org.springframework.core.io.ByteArrayResource; public class ZookeeperClient { static class ZookeeperSASLConfig extends Configuration { AppConfigurationEntry entry; public ZookeeperSASLConfig(String username, String password) { Map options = new HashMap<>(); options.put("username", username); options.put("password", password); this.entry = new AppConfigurationEntry( DigestLoginModule.class.getName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options ); } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { AppConfigurationEntry[] array = new AppConfigurationEntry[1]; array[0] = entry; return array; } } private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperClient.class); public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; private final UpdateHandler updateHandler; private final ZookeeperConfig zookeeperConfig; private final Environment environment; private final Object lock = new Object(); private Map environmentData = new HashMap<>(); private Map applicationData = new HashMap<>(); private Map serviceData = new HashMap<>(); private Map versionData = new HashMap<>(); private Map tagData = new HashMap<>(); private Map allLast = new HashMap<>(); public ZookeeperClient(UpdateHandler updateHandler, Environment environment) { this.updateHandler = updateHandler; this.zookeeperConfig = new ZookeeperConfig(environment); this.environment = environment; } public void refreshZookeeperConfig() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(zookeeperConfig.getConnectString()) .sessionTimeoutMs(zookeeperConfig.getSessionTimeoutMillis()) .retryPolicy(new ExponentialBackoffRetry(1000, 3)); String authSchema = zookeeperConfig.getAuthSchema(); if (StringUtils.isNotEmpty(authSchema)) { if (!"digest".equals(authSchema)) { throw new IllegalStateException("Not supported schema now. " + authSchema); } if (zookeeperConfig.getAuthInfo() == null) { throw new IllegalStateException("Auth info can not be empty. "); } String[] authInfo = zookeeperConfig.getAuthInfo().split(":"); Configuration.setConfiguration(new ZookeeperSASLConfig(authInfo[0], authInfo[1])); } CuratorFramework client = builder.build(); client.start(); String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = ZookeeperConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; } addEnvironmentConfig(env, client); addApplicationConfig(env, client); addServiceConfig(env, client); addVersionConfig(env, client); addTagConfig(env, client); refreshConfigItems(); } private void addTagConfig(String env, CuratorFramework client) throws Exception { if (StringUtils.isEmpty(zookeeperConfig.getInstanceTag())) { return; } String path = String.format(PATH_TAG, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment), zookeeperConfig.getInstanceTag()); CuratorCache cache = CuratorCache.builder(client, path).build(); cache.listenable().addListener((type, oldData, newData) -> { try { this.tagData = parseData(client, path); refreshConfigItems(); } catch (Exception e) { LOGGER.error("process event failed", e); } }); cache.start(); this.tagData = parseData(client, path); } private void addVersionConfig(String env, CuratorFramework client) throws Exception { String path = String.format(PATH_VERSION, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment)); CuratorCache cache = CuratorCache.builder(client, path).build(); cache.listenable().addListener((type, oldData, newData) -> { try { this.versionData = parseData(client, path); refreshConfigItems(); } catch (Exception e) { LOGGER.error("process event failed", e); } }); cache.start(); this.versionData = parseData(client, path); } private void addServiceConfig(String env, CuratorFramework client) throws Exception { String path = String.format(PATH_SERVICE, env, BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment)); CuratorCache cache = CuratorCache.builder(client, path).build(); cache.listenable().addListener((type, oldData, newData) -> { try { this.serviceData = parseData(client, path); refreshConfigItems(); } catch (Exception e) { LOGGER.error("process event failed", e); } }); cache.start(); this.serviceData = parseData(client, path); } private void addApplicationConfig(String env, CuratorFramework client) throws Exception { String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); CuratorCache cache = CuratorCache.builder(client, path).build(); cache.listenable().addListener((type, oldData, newData) -> { try { this.applicationData = parseData(client, path); refreshConfigItems(); } catch (Exception e) { LOGGER.error("process event failed", e); } }); cache.start(); this.applicationData = parseData(client, path); } private void addEnvironmentConfig(String env, CuratorFramework client) throws Exception { String path = String.format(PATH_ENVIRONMENT, env); CuratorCache cache = CuratorCache.builder(client, path).build(); cache.listenable().addListener((type, oldData, newData) -> { try { this.environmentData = parseData(client, path); refreshConfigItems(); } catch (Exception e) { LOGGER.error("process event failed", e); } }); cache.start(); this.environmentData = parseData(client, path); } private Map parseData(CuratorFramework client, String path) throws Exception { Map values = new HashMap<>(); if (client.checkExists().forPath(path) != null) { client.getChildren().forPath(path).stream().sorted().forEach(item -> { try { byte[] data = client.getData().forPath(path + "/" + item); if (item.endsWith(".yaml") || item.endsWith(".yml")) { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(data)); values.putAll(toMap(yamlFactory.getObject())); } else if (item.endsWith(".properties")) { Properties properties = new Properties(); properties.load(new StringReader(new String(data, StandardCharsets.UTF_8))); values.putAll(toMap(properties)); } else { values.put(item, new String(data, StandardCharsets.UTF_8)); } } catch (Exception e) { LOGGER.error("", e); } }); } return values; } private void refreshConfigItems() { synchronized (lock) { Map all = new HashMap<>(); all.putAll(environmentData); all.putAll(applicationData); all.putAll(serviceData); all.putAll(versionData); all.putAll(tagData); updateHandler.handle(all, allLast); this.allLast = all; } } @SuppressWarnings("unchecked") private Map toMap(Properties properties) { if (properties == null) { return Collections.emptyMap(); } Map result = new HashMap<>(); Enumeration keys = (Enumeration) properties.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Object value = properties.getProperty(key); result.put(key, value); } return result; } } ================================================ FILE: dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.zookeeper; import org.springframework.core.env.Environment; public class ZookeeperConfig { public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.zk.connect-string"; public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.zk.session-timeout-millis"; public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.zk.connection-timeout-mills"; public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.zk.authentication-schema"; public static final String PROPERTY_AUTH_INFO = "servicecomb.config.zk.authentication-info"; public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.zk.instance-tag"; private final Environment environment; public ZookeeperConfig(Environment environment) { this.environment = environment; } public String getConnectString() { return environment.getProperty(PROPERTY_CONNECT_STRING, "127.0.0.1:2181"); } public int getSessionTimeoutMillis() { return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); } public int getConnectionTimeoutMillis() { return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); } public String getAuthSchema() { return environment.getProperty(PROPERTY_AUTH_SCHEMA); } public String getAuthInfo() { return environment.getProperty(PROPERTY_AUTH_INFO); } public String getInstanceTag() { return environment.getProperty(PROPERTY_INSTANCE_TAG); } } ================================================ FILE: dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.zookeeper; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.DynamicPropertiesSource; import org.apache.servicecomb.foundation.common.event.EventManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; public class ZookeeperDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "zookeeper"; private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperDynamicPropertiesSource.class); private final Map valueCache = new ConcurrentHashMap<>(); public ZookeeperDynamicPropertiesSource() { } private final UpdateHandler updateHandler = new UpdateHandler(); private void init(Environment environment) { ZookeeperClient zookeeperClient = new ZookeeperClient(updateHandler, environment); try { zookeeperClient.refreshZookeeperConfig(); } catch (Exception e) { throw new IllegalStateException("Set up zookeeper config failed.", e); } } public class UpdateHandler { public void handle(Map current, Map last) { ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); valueCache.putAll(event.getAdded()); valueCache.putAll(event.getUpdated()); event.getDeleted().forEach((k, v) -> valueCache.remove(k)); EventManager.post(event); } } @Override public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, valueCache); } @Override public int getOrder() { return 0; } } ================================================ FILE: dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.zookeeper.ZookeeperDynamicPropertiesSource ================================================ FILE: dynamic-config/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 dynamic-config Java Chassis::Dynamic Config pom config-apollo config-cc config-nacos config-kie config-zookeeper config-etcd config-consul ================================================ FILE: edge/edge-core/pom.xml ================================================ 4.0.0 org.apache.servicecomb edge 3.4.0-SNAPSHOT edge-core Java Chassis::Edge::Core org.apache.servicecomb transport-rest-vertx org.apache.servicecomb handler-loadbalance io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test org.apache.servicecomb swagger-generator-jaxrs test org.mockito mockito-inline test ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/AbstractEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.apache.servicecomb.transport.rest.vertx.AbstractVertxHttpDispatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public abstract class AbstractEdgeDispatcher extends AbstractVertxHttpDispatcher { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractEdgeDispatcher.class); protected void onFailure(RoutingContext context) { LOGGER.error("edge server failed.", context.failure()); HttpServerResponse response = context.response(); if (response.closed() || response.ended()) { return; } if (context.failure() instanceof InvocationException) { InvocationException exception = (InvocationException) context.failure(); response.setStatusCode(exception.getStatusCode()); response.setStatusMessage(exception.getReasonPhrase()); if (null == exception.getErrorData()) { response.end(); return; } String responseBody; try { responseBody = RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(exception.getErrorData()); response.putHeader("Content-Type", MediaType.APPLICATION_JSON); } catch (JsonProcessingException e) { responseBody = exception.getErrorData().toString(); response.putHeader("Content-Type", MediaType.TEXT_PLAIN); } response.end(responseBody); } else { response.setStatusCode(Status.BAD_GATEWAY.getStatusCode()); response.setStatusMessage(Status.BAD_GATEWAY.getReasonPhrase()); response.end(); } } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.loadbalance.ExtensionsManager; import org.apache.servicecomb.loadbalance.LoadBalanceFilter; import org.apache.servicecomb.loadbalance.LoadBalancer; import org.apache.servicecomb.loadbalance.RuleExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI; import org.apache.servicecomb.transport.rest.client.HttpTransportHttpClientOptionsSPI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; import io.vertx.core.http.RequestOptions; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; /** * This dispatcher forwards requests to any http servers, includes java-chassis providers and other, * provided the server is registered to service center. * * This dispatcher using loadbalance handler to choose the target server. So any functions * provided by loadbalancer handler is available, excluding retrying. */ public class CommonHttpEdgeDispatcher extends AbstractEdgeDispatcher { private static final Logger LOG = LoggerFactory.getLogger(CommonHttpEdgeDispatcher.class); private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.http.enabled"; private static final String KEY_ORDER = "servicecomb.http.dispatcher.edge.http.order"; private static final String KEY_PATTERN = "servicecomb.http.dispatcher.edge.http.pattern"; private static final String PATTERN_ANY = "/(.*)"; private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.http.mappings"; private final Map loadBalancerMap = new ConcurrentHashMapEx<>(); private Map configurations = new HashMap<>(); private Environment environment; public CommonHttpEdgeDispatcher() { } // though this is an SPI, but add as beans. @Autowired public void setEnvironment(Environment environment) { this.environment = environment; if (this.enabled()) { loadConfigurations(); } } // Maybe future change to beans protected DiscoveryTree getDiscoveryTree() { return BeanUtils.getBean(DiscoveryTree.class); } // Maybe future change to beans protected Environment getEnvironment() { return BeanUtils.getBean(Environment.class); } // Maybe future change to beans protected ExtensionsManager getExtensionsManager() { return BeanUtils.getBean(ExtensionsManager.class); } @Override public int getOrder() { return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 40_000); } @Override public boolean enabled() { return environment.getProperty(KEY_ENABLED, boolean.class, false); } @Override public void init(Router router) { String pattern = environment.getProperty(KEY_PATTERN, PATTERN_ANY); router.routeWithRegex(pattern).failureHandler(this::onFailure).handler(this::onRequest); } private void loadConfigurations() { configurations = URLMappedConfigurationLoader.loadConfigurations(environment, KEY_MAPPING_PREFIX); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(KEY_MAPPING_PREFIX)) { loadConfigurations(); break; } } } protected void onRequest(RoutingContext context) { URLMappedConfigurationItem configurationItem = findConfigurationItem(context.request().uri()); if (configurationItem == null) { context.next(); return; } String uri = Utils.findActualPath(context.request().uri(), configurationItem.getPrefixSegmentCount()); Invocation invocation = new Invocation() { @Override public String getConfigTransportName() { return "rest"; } @Override public String getMicroserviceName() { return configurationItem.getMicroserviceName(); } }; LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation, configurationItem.getMicroserviceName() ); ServiceCombServer server = loadBalancer.chooseServer(invocation); if (server == null) { LOG.warn("no available server for service {}", configurationItem.getMicroserviceName()); serverNotReadyResponse(context); return; } URIEndpointObject endpointObject = new URIEndpointObject(server.getEndpoint().getEndpoint()); RequestOptions requestOptions = new RequestOptions(); requestOptions.setHost(endpointObject.getHostOrIp()) .setPort(endpointObject.getPort()) .setSsl(endpointObject.isSslEnabled()) .setMethod(context.request().method()) .setURI(uri); HttpClient httpClient; if (endpointObject.isHttp2Enabled()) { httpClient = HttpClients.getClient(Http2TransportHttpClientOptionsSPI.CLIENT_NAME, false).getHttpClient(); } else { httpClient = HttpClients.getClient(HttpTransportHttpClientOptionsSPI.CLIENT_NAME, false).getHttpClient(); } context.request().pause(); httpClient .request(requestOptions).compose(httpClientRequest -> { context.request().headers() .forEach((header) -> httpClientRequest.headers().set(header.getKey(), header.getValue())); context.request().resume(); context.request().handler(httpClientRequest::write); context.request().endHandler((v) -> httpClientRequest.end()); return httpClientRequest.response().compose(httpClientResponse -> { context.response().setStatusCode(httpClientResponse.statusCode()); httpClientResponse.headers() .forEach((header) -> context.response().headers().set(header.getKey(), header.getValue())); httpClientResponse.handler(this.responseHandler(context)); httpClientResponse.endHandler((v) -> context.response().end()); return Future.succeededFuture(); }); }).onFailure(failure -> { LOG.warn("send request to target {}:{} failed, cause {}", endpointObject.getHostOrIp(), endpointObject.getPort(), failure.getMessage()); serverNotReadyResponse(context); }); } private void serverNotReadyResponse(RoutingContext context) { context.response().setStatusCode(503); context.response().setStatusMessage("service not ready"); context.response().end(); } protected Handler responseHandler(RoutingContext routingContext) { return data -> routingContext.response().write(data); } protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation, String microserviceName) { DiscoveryContext context = new DiscoveryContext(); context.setInputParameters(invocation); VersionedCache serversVersionedCache = getDiscoveryTree().discovery(context, BootStrapProperties.readApplication(environment), microserviceName); invocation.addLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST, serversVersionedCache.data()); return loadBalancerMap .computeIfAbsent(microserviceName, name -> createLoadBalancer(microserviceName)); } private LoadBalancer createLoadBalancer(String microserviceName) { RuleExt rule = getExtensionsManager().createLoadBalancerRule(microserviceName); return new LoadBalancer(rule, microserviceName); } private URLMappedConfigurationItem findConfigurationItem(String path) { for (URLMappedConfigurationItem item : configurations.values()) { if (item.getPattern().matcher(path).matches()) { return item; } } return null; } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; import io.vertx.codegen.annotations.Nullable; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; /** * Provide an easy mapping dispatcher that starts with a common prefix pattern. */ public class DefaultEdgeDispatcher extends AbstractEdgeDispatcher { private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.default.enabled"; private static final String KEY_ORDER = "servicecomb.http.dispatcher.edge.default.order"; private static final String KEY_PREFIX = "servicecomb.http.dispatcher.edge.default.prefix"; private static final String KEY_PREFIX_SEGMENT_COUNT = "servicecomb.http.dispatcher.edge.default.prefixSegmentCount"; public static final String MICROSERVICE_NAME = "param0"; private int prefixSegmentCount; private Environment environment; // though this is an SPI, but add as beans. @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public int getOrder() { // can not use environment, add as beans is later than instantiate SPI return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 20_000); } @Override public boolean enabled() { return environment.getProperty(KEY_ENABLED, boolean.class, false); } @Override public void init(Router router) { String prefix = environment.getProperty(KEY_PREFIX, "api"); prefixSegmentCount = environment.getProperty(KEY_PREFIX_SEGMENT_COUNT, int.class, 1); String regex = generateRouteRegex(prefix, false); // cookies handler are enabled by default start from 3.8.3 router.routeWithRegex(regex).handler(createBodyHandler()); router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest); } @VisibleForTesting String generateRouteRegex(String prefix, boolean withVersion) { String version = withVersion ? "/([^\\\\/]+)" : ""; return String.format("/%s/([^\\\\/]+)%s/(.*)", prefix, version); } protected void onRequest(RoutingContext context) { String microserviceName = extractMicroserviceName(context); String path = Utils.findActualPath(context.request().path(), prefixSegmentCount); requestByFilter(context, microserviceName, path); } @Nullable private String extractMicroserviceName(RoutingContext context) { return context.pathParam(MICROSERVICE_NAME); } protected void requestByFilter(RoutingContext context, String microserviceName, String path) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, microserviceName, path); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.transport.rest.client.RestClientTransportContext; import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; public class EdgeAddHeaderFilter extends AbstractFilter implements EdgeFilter { public static final String NAME = "edge-add-headers"; private static final String PREFIX = "servicecomb.edge.filter.addHeader"; private static final String KEY_HEADERS = PREFIX + ".allowedHeaders"; private final Environment environment; private List publicHeaders = new ArrayList<>(); public EdgeAddHeaderFilter(Environment environment) { this.environment = environment; init(); EventManager.register(this); } @Subscribe @SuppressWarnings("unused") public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(PREFIX)) { init(); break; } } } private void init() { String publicHeaderStr = environment.getProperty(KEY_HEADERS); if (StringUtils.isEmpty(publicHeaderStr)) { return; } publicHeaders = Arrays.asList(publicHeaderStr.split(",")); } @Override public String getName() { return NAME; } @Override public boolean enabledForTransport(String transport) { return CoreConst.RESTFUL.equals(transport); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1995; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (publicHeaders.isEmpty()) { return nextNode.onFilter(invocation); } RestClientTransportContext transportContext = invocation.getTransportContext(); HttpServletRequestEx oldRequest = invocation.getRequestEx(); publicHeaders.forEach(key -> { String value = oldRequest.getHeader(key); if (StringUtils.isNotEmpty(value)) { transportContext.getHttpClientRequest().putHeader(key, value); } }); return nextNode.onFilter(invocation); } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.transport.rest.vertx.TransportConfig; public class EdgeBootListener implements BootListener { @Override public void onBootEvent(BootEvent event) { if (!EventType.BEFORE_PRODUCER_PROVIDER.equals(event.getEventType())) { return; } TransportConfig.setRestServerVerticle(EdgeRestServerVerticle.class); ExecutorManager.setExecutorDefault(ExecutorManager.EXECUTOR_REACTIVE); } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class EdgeCoreConfiguration { @Bean public EdgeBootListener scbEdgeBootListener() { return new EdgeBootListener(); } @Bean public EdgeAddHeaderFilter scbEdgeAddHeaderFilter(Environment environment) { return new EdgeAddHeaderFilter(environment); } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.common.rest.RestVertxProducerInvocationCreator; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import io.vertx.core.Vertx; import io.vertx.ext.web.RoutingContext; public class EdgeInvocationCreator extends RestVertxProducerInvocationCreator { public static final String EDGE_INVOCATION_CONTEXT = "edgeInvocationContext"; protected final String microserviceName; protected final String path; protected MicroserviceReferenceConfig microserviceReferenceConfig; public EdgeInvocationCreator(RoutingContext routingContext, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx, String microserviceName, String path) { // Set endpoint before load balance because edge service will use RESTFUL transport filters. super(routingContext, null, SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL).getEndpoint(), requestEx, responseEx); this.microserviceName = microserviceName; this.path = path; } @Override public CompletableFuture createAsync() { return createMicroserviceReferenceConfig() .thenCompose(v -> super.createAsync()); } protected CompletableFuture createMicroserviceReferenceConfig() { return SCBEngine.getInstance() .getOrCreateReferenceConfigAsync(microserviceName) .thenAccept(mrc -> { this.microserviceReferenceConfig = mrc; this.microserviceMeta = mrc.getMicroserviceMeta(); }); } @Override protected OperationLocator locateOperation(ServicePathManager servicePathManager) { return servicePathManager.consumerLocateOperation(path, requestEx.getMethod()); } @Override protected Invocation createInstance() { ReferenceConfig referenceConfig = microserviceReferenceConfig .createReferenceConfig(restOperationMeta.getOperationMeta()); Invocation invocation = InvocationFactory.forConsumer(referenceConfig, restOperationMeta.getOperationMeta(), restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(), null); invocation.setSync(false); invocation.setEdge(); invocation.setEndpoint(endpoint); // ensure transport name is correct invocation.addLocalContext(EDGE_INVOCATION_CONTEXT, Vertx.currentContext()); return invocation; } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestServerVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.transport.rest.vertx.RestServerVerticle; public class EdgeRestServerVerticle extends RestServerVerticle { @Override public void start() throws Exception { super.start(); } } ================================================ FILE: edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.PlatformHandler; /** * Provide a URL mapping based dispatcher. Users configure witch URL patterns dispatch to a target service. */ public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher { public static final String CONFIGURATION_ITEM = "URLMappedConfigurationItem"; private static final String PATTERN_ANY = "/(.*)"; private static final String KEY_ORDER = "servicecomb.http.dispatcher.edge.url.order"; private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.url.enabled"; private static final String KEY_PATTERN = "servicecomb.http.dispatcher.edge.url.pattern"; private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.url.mappings"; private Map configurations = new HashMap<>(); private Environment environment; public URLMappedEdgeDispatcher() { EventManager.register(this); } // though this is an SPI, but add as beans. @Autowired public void setEnvironment(Environment environment) { this.environment = environment; if (this.enabled()) { loadConfigurations(); } } @VisibleForTesting public Map getConfigurations() { return configurations; } @Override public int getOrder() { return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 30_000); } @Override public boolean enabled() { return environment.getProperty(KEY_ENABLED, boolean.class, false); } @Override public void init(Router router) { // cookies handler are enabled by default start from 3.8.3 String pattern = environment.getProperty(KEY_PATTERN, PATTERN_ANY); router.routeWithRegex(pattern).failureHandler(this::onFailure) .handler((PlatformHandler) URLMappedEdgeDispatcher.this::preCheck) .handler(createBodyHandler()) .handler(this::onRequest); } private void loadConfigurations() { configurations = URLMappedConfigurationLoader.loadConfigurations(environment, KEY_MAPPING_PREFIX); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(KEY_MAPPING_PREFIX)) { loadConfigurations(); break; } } } protected void preCheck(RoutingContext context) { URLMappedConfigurationItem configurationItem = findConfigurationItem(context.request().path()); if (configurationItem == null) { // by pass body handler flag context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.TRUE); context.next(); return; } context.put(CONFIGURATION_ITEM, configurationItem); context.next(); } protected void onRequest(RoutingContext context) { Boolean bypass = context.get(RestBodyHandler.BYPASS_BODY_HANDLER); if (Boolean.TRUE.equals(bypass)) { // clear flag context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.FALSE); context.next(); return; } URLMappedConfigurationItem configurationItem = context.get(CONFIGURATION_ITEM); String path = Utils.findActualPath(context.request().path(), configurationItem.getPrefixSegmentCount()); requestByFilter(context, configurationItem, path); } protected void requestByFilter(RoutingContext context, URLMappedConfigurationItem configurationItem, String path) { HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, configurationItem.getMicroserviceName(), path); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } private URLMappedConfigurationItem findConfigurationItem(String path) { for (URLMappedConfigurationItem item : configurations.values()) { if (item.getPattern().matcher(path).matches()) { return item; } } return null; } } ================================================ FILE: edge/edge-core/src/main/resources/META-INF/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: -500 servicecomb: context: # do not decode invocation context for edge service by default decodeInvocationContext: false ================================================ FILE: edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.edge.core.DefaultEdgeDispatcher org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher ================================================ FILE: edge/edge-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.edge.core.EdgeCoreConfiguration ================================================ FILE: edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestAbstractEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestAbstractEdgeDispatcher { static class AbstractEdgeDispatcherForTest extends AbstractEdgeDispatcher { @Override public int getOrder() { return 0; } @Override public void init(Router router) { } } @Test public void onFailure() { RoutingContext context = Mockito.mock(RoutingContext.class); HttpServerResponse response = Mockito.mock(HttpServerResponse.class); Mockito.when(context.response()).thenReturn(response); Mockito.when(context.failure()).thenReturn(new RuntimeExceptionWithoutStackTrace("failed")); AbstractEdgeDispatcherForTest dispatcher = new AbstractEdgeDispatcherForTest(); dispatcher.onFailure(context); Mockito.verify(response).setStatusMessage("Bad Gateway"); Mockito.verify(response).setStatusCode(502); Mockito.when(context.failure()).thenReturn(new InvocationException(401, "unauthorized", "unauthorized")); dispatcher = new AbstractEdgeDispatcherForTest(); dispatcher.onFailure(context); Mockito.verify(response).setStatusMessage("unauthorized"); Mockito.verify(response).setStatusCode(401); } } ================================================ FILE: edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; import io.vertx.ext.web.RoutingContext; public class TestURLMappedEdgeDispatcher { ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); @BeforeEach public void setUp() throws Exception { } @AfterEach public void tearDown() { } @Test public void testConfigurations() { EnumerablePropertySource propertySource = Mockito.mock(EnumerablePropertySource.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { }); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.enabled", boolean.class, false)) .thenReturn(true); URLMappedEdgeDispatcher dispatcher = new URLMappedEdgeDispatcher(); dispatcher.setEnvironment(environment); Map items = dispatcher.getConfigurations(); Assertions.assertEquals(items.size(), 0); RoutingContext context = Mockito.mock(RoutingContext.class); Mockito.when(context.get(RestBodyHandler.BYPASS_BODY_HANDLER)).thenReturn(Boolean.TRUE); dispatcher.onRequest(context); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.http.dispatcher.edge.url.mappings.service1.path", "servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName", "servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", "servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule" }); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.path")) .thenReturn("/a/b/c/.*"); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName")) .thenReturn("serviceName"); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", int.class, 0)) .thenReturn(2); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule", "0.0.0+")) .thenReturn("2.0.0+"); Map latest = new HashMap<>(); latest.put("servicecomb.http.dispatcher.edge.url.mappings.service1.path", "/a/b/c/.*"); dispatcher.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); items = dispatcher.getConfigurations(); Assertions.assertEquals(items.size(), 1); URLMappedConfigurationItem item = items.get("service1"); Assertions.assertEquals(item.getMicroserviceName(), "serviceName"); Assertions.assertEquals(item.getPrefixSegmentCount(), 2); Assertions.assertEquals(item.getStringPattern(), "/a/b/c/.*"); Assertions.assertEquals(item.getVersionRule(), "2.0.0+"); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.http.dispatcher.edge.url.mappings.service1.path", "servicecomb.http.dispatcher.edge.url.mappings.service1.microserviceName", "servicecomb.http.dispatcher.edge.url.mappings.service1.prefixSegmentCount", "servicecomb.http.dispatcher.edge.url.mappings.service1.versionRule", "servicecomb.http.dispatcher.edge.url.mappings.service2.versionRule", "servicecomb.http.dispatcher.edge.url.mappings.service3.path" }); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service2.versionRule")) .thenReturn("2.0.0+"); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.edge.url.mappings.service3.path")) .thenReturn("/b/c/d/.*"); latest = new HashMap<>(); latest.put("servicecomb.http.dispatcher.edge.url.mappings.service3.path", "/a/b/c/.*"); dispatcher.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); items = dispatcher.getConfigurations(); Assertions.assertEquals(items.size(), 1); item = items.get("service1"); Assertions.assertEquals(item.getMicroserviceName(), "serviceName"); Assertions.assertEquals(item.getPrefixSegmentCount(), 2); Assertions.assertEquals(item.getStringPattern(), "/a/b/c/.*"); Assertions.assertEquals(item.getVersionRule(), "2.0.0+"); } } ================================================ FILE: edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.common.rest.route.Utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestUtils { @Test public void testUtils() { Assertions.assertEquals("/a/b/c", Utils.findActualPath("/a/b/c", -1)); Assertions.assertEquals("/a/b/c", Utils.findActualPath("/a/b/c", 0)); Assertions.assertEquals("/b/c", Utils.findActualPath("/a/b/c", 1)); Assertions.assertEquals("/c", Utils.findActualPath("/a/b/c", 2)); Assertions.assertEquals("", Utils.findActualPath("/a/b/c", 3)); Assertions.assertEquals("", Utils.findActualPath("/a/b/c", 100)); } } ================================================ FILE: edge/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default edge Java Chassis::Edge pom edge-core ================================================ FILE: etc/code-templates.xml ================================================ ================================================ FILE: etc/eclipse-java-google-style.xml ================================================ ================================================ FILE: etc/intellij-java-google-style.xml ================================================ ================================================ FILE: foundations/foundation-common/pom.xml ================================================ 4.0.0 org.apache.servicecomb 3.4.0-SNAPSHOT foundations foundation-common Java Chassis::Foundations::Common com.sun.activation jakarta.activation com.fasterxml.jackson.dataformat jackson-dataformat-xml com.fasterxml.jackson.core jackson-databind com.fasterxml.jackson.core jackson-annotations org.springframework spring-context org.slf4j slf4j-api org.apache.httpcomponents httpclient commons-logging commons-logging jakarta.ws.rs jakarta.ws.rs-api commons-io commons-io com.google.guava guava com.google.guava failureaccess jakarta.servlet jakarta.servlet-api org.apache.commons commons-lang3 org.apache.servicecomb foundation-spi org.apache.servicecomb foundation-test-scaffolding com.fasterxml.jackson.datatype jackson-datatype-jsr310 io.vertx vertx-core org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; public interface Cipher { String name(); char[] decrypt(char[] encrypted); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; public final class DefaultCipher implements Cipher { public static final String CIPHER_NAME = "default"; private static final DefaultCipher INSTANCE = new DefaultCipher(); public static DefaultCipher getInstance() { return INSTANCE; } private DefaultCipher() { } @Override public String name() { return CIPHER_NAME; } @Override public char[] decrypt(char[] encrypted) { return encrypted; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; import org.springframework.context.annotation.Configuration; @Configuration public class FoundationCommonAuthConfiguration { public ShaAKSKCipher scbShaAKSKCipher() { return new ShaAKSKCipher(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/ShaAKSKCipher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; public class ShaAKSKCipher implements Cipher { public static final String CIPHER_NAME = "ShaAKSKCipher"; @Override public String name() { return CIPHER_NAME; } @Override public char[] decrypt(char[] encrypted) { return encrypted; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/AbstractObjectManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractObjectManager { protected Map objMap = new ConcurrentHashMap<>(); protected final Object lockObj = new Object(); public VALUE getOrCreate(KEY_OWNER keyOwner) { KEY key = getKey(keyOwner); VALUE value = objMap.get(key); if (value == null) { synchronized (lockObj) { value = objMap.get(key); if (value == null) { value = create(keyOwner); if (value == null) { // 创建失败,下次重新创建 return null; } objMap.put(key, value); } } } return value; } public VALUE findByKey(KEY key) { return objMap.get(key); } public VALUE findByContainer(KEY_OWNER keyOwner) { KEY key = getKey(keyOwner); return objMap.get(key); } public Set keys() { return objMap.keySet(); } public Collection values() { return objMap.values(); } protected abstract KEY getKey(KEY_OWNER keyOwner); /** * 只会在锁的保护下执行 */ protected abstract VALUE create(KEY_OWNER keyOwner); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/CommonThread.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; public class CommonThread extends Thread { protected volatile boolean shutdown; public CommonThread() { super(); } public CommonThread(String name, long stackSize) { super(null, null, name, stackSize); } public boolean isShutdown() { return shutdown; } public boolean isRunning() { return !shutdown; } public void shutdown() { shutdown = true; interrupt(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/DynamicObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; public class DynamicObject { protected Map dynamic = new HashMap<>(); @JsonAnyGetter public Map getDynamic() { return dynamic; } @SuppressWarnings("unchecked") public T getDynamic(String key) { return (T) dynamic.get(key); } public void setDynamic(Map dynamic) { this.dynamic = dynamic; } @JsonAnySetter public DynamicObject putDynamic(String key, Object value) { this.dynamic.put(key, value); return this; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Holder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; // do not use javax.xml.ws.Holder, use this one. Because JDK 11 above do not have jakarta.ws.Holder public final class Holder { /** * The value contained in the holder. */ public T value; /** * Creates a new holder with a null value. */ public Holder() { } /** * Create a new holder with the specified value. * * @param value The value to be stored in the holder. */ public Holder(T value) { this.value = value; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/LegacyPropertyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; /** * Provider a convenient way to get property value in static context. * * NOTE: This way is not commented and only for legacy code without too much refactoring. * And make sure to use this class when spring bean context is already initialized. */ public class LegacyPropertyFactory { private static Environment environment; public LegacyPropertyFactory(Environment environment) { LegacyPropertyFactory.environment = environment; } @VisibleForTesting public static void setEnvironment(Environment environment) { LegacyPropertyFactory.environment = environment; } public static T getProperty(String key, Class targetType) { return environment.getProperty(key, targetType); } public static T getProperty(String key, Class targetType, T defaultValue) { return environment.getProperty(key, targetType, defaultValue); } public static boolean getBooleanProperty(String key, boolean defaultValue) { return environment.getProperty(key, boolean.class, defaultValue); } public static int getIntProperty(String key, int defaultValue) { return environment.getProperty(key, int.class, defaultValue); } public static long getLongProperty(String key, long defaultValue) { return environment.getProperty(key, long.class, defaultValue); } public static String getStringProperty(String key) { return environment.getProperty(key); } public static String getStringProperty(String key, String defaultValue) { return environment.getProperty(key, defaultValue); } public static Environment getEnvironment() { return environment; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * 已命名线程工厂. * 获取新的名字有固定前缀的线程工厂. */ public class NamedThreadFactory implements ThreadFactory { private final AtomicInteger threadNumber = new AtomicInteger(); private String prefix; public NamedThreadFactory() { this("Thread"); } public NamedThreadFactory(String prefix) { this.prefix = prefix; } /** * 获取新的名字以prefix为前缀的线程 */ @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); thread.setDaemon(true); return thread; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Objects; import java.util.StringJoiner; /** * Utility class for creating ParameterizedType. */ public class ParameterizedTypeUtil implements ParameterizedType { private final Type[] actualTypeArguments; private final Class rawType; private ParameterizedTypeUtil(Class rawType, Type[] actualTypeArguments) { this.actualTypeArguments = actualTypeArguments; this.rawType = rawType; } public static ParameterizedType make(Class rawType, Type... actualTypeArguments) { return new ParameterizedTypeUtil(rawType, actualTypeArguments); } @Override public Type[] getActualTypeArguments() { return this.actualTypeArguments; } @Override public Type getRawType() { return rawType; } @Override public Type getOwnerType() { return null; } @Override public boolean equals(Object o) { if (o instanceof ParameterizedType that) { if (this == that) { return true; } Type thatRawType = that.getRawType(); return Objects.equals(rawType, thatRawType) && Arrays.equals(actualTypeArguments, that.getActualTypeArguments()); } else { return false; } } @Override public int hashCode() { return Arrays.hashCode(actualTypeArguments) ^ Objects.hashCode(rawType); } public String toString() { StringBuilder sb = new StringBuilder(); sb.append(rawType.getName()); if (actualTypeArguments != null) { StringJoiner sj = new StringJoiner(", ", "<", ">"); sj.setEmptyValue(""); for (Type t : actualTypeArguments) { sj.add(t.getTypeName()); } sb.append(sj); } return sb.toString(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/RegisterManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RegisterManager { private static final Logger LOGGER = LoggerFactory.getLogger(RegisterManager.class); private final String name; private String registerErrorFmt = "Not allow register repeat data, name=%s, key=%s"; private final Map objMap = new ConcurrentHashMap<>(); private final Object lockObj = new Object(); protected Map getObjMap() { return objMap; } public RegisterManager(String name) { this.name = name; } public String getName() { return name; } public String getRegisterErrorFmt() { return registerErrorFmt; } public void setRegisterErrorFmt(String registerErrorFmt) { this.registerErrorFmt = registerErrorFmt; } public void register(KEY key, VALUE value) { synchronized (lockObj) { if (objMap.get(key) != null) { // 不允许重复注册 String msg = String.format(registerErrorFmt, name, key); LOGGER.error(msg); // 禁止启动 throw new Error(msg); } objMap.put(key, value); } } public VALUE findValue(KEY key) { return objMap.get(key); } public VALUE ensureFindValue(KEY key) { VALUE value = objMap.get(key); if (value == null) { String msg = String.format("Can not find value, name=%s, key=%s", name, key); throw new Error(msg); } return value; } public Collection keys() { return objMap.keySet(); } public Collection values() { return objMap.values(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/VendorExtensions.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.util.Map; import java.util.function.Function; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class VendorExtensions { private final Map store = new ConcurrentHashMapEx<>(); public Map getStore() { return store; } @SuppressWarnings("unchecked") public V get(Object key) { return (V) store.get(key); } public void put(Object key, Object value) { store.put(key, value); } @SuppressWarnings("unchecked") public V computeIfAbsent(Object key, Function mappingFunction) { return (V) store.computeIfAbsent(key, mappingFunction); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Version.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import java.util.Objects; import org.apache.commons.lang3.ArrayUtils; // short version is enough public class Version implements Comparable { private static final String[] ZERO = new String[] {"0", "0", "0", "0"}; private final short major; private final short minor; private final short patch; private final short build; private final String version; private final long numberVersion; // 1 // 1.0 // 1.0.0 // 1.0.0.0 public Version(String version) { Objects.requireNonNull(version); String[] versions = version.split("\\.", -1); if (versions.length > 4) { throw new IllegalStateException(String.format("Invalid version \"%s\".", version)); } if (versions.length < 4) { versions = ArrayUtils.addAll(versions, ZERO); } this.major = parseNumber("major", version, versions[0]); this.minor = parseNumber("minor", version, versions[1]); this.patch = parseNumber("patch", version, versions[2]); this.build = parseNumber("build", version, versions[3]); this.version = combineStringVersion(); this.numberVersion = combineVersion(); } private short parseNumber(String name, String allVersion, String version) { short value = 0; try { value = Short.parseShort(version); } catch (NumberFormatException e) { throw new IllegalStateException( String.format("Invalid %s \"%s\", version \"%s\".", name, version, allVersion), e); } if (value < 0) { throw new IllegalStateException( String.format("%s \"%s\" can not be negative, version \"%s\".", name, version, allVersion)); } return value; } public Version(short major, short minor, short patch, short build) { this.major = major; this.minor = minor; this.patch = patch; this.build = build; this.version = combineStringVersion(); this.numberVersion = combineVersion(); } private String combineStringVersion() { return major + "." + minor + "." + patch + "." + build; } // 1.0.0 equals 1.0.0.0 private long combineVersion() { return (long) major << 48 | (long) minor << 32 | (long) patch << 16 | (long) build; } public short getMajor() { return major; } public short getMinor() { return minor; } public short getPatch() { return patch; } public short getBuild() { return build; } public String getVersion() { return version; } @Override public String toString() { return version; } @Override public int hashCode() { return version.hashCode(); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Version)) { return false; } return numberVersion == ((Version) other).numberVersion; } @Override public int compareTo(Version other) { return Long.compare(numberVersion, other.numberVersion); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DescriptiveRunnable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.base; public interface DescriptiveRunnable extends Runnable { String description(); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.base; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonValue; public abstract class DynamicEnum { private final T value; @JsonIgnore private boolean dynamic = false; public DynamicEnum(T value) { this.value = value; } @JsonValue public T getValue() { return value; } @JsonIgnore public boolean isStatic() { return !dynamic; } public boolean isDynamic() { return dynamic; } public DynamicEnum setDynamic(boolean dynamic) { this.dynamic = dynamic; return this; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DynamicEnum that = (DynamicEnum) o; return Objects.equals(value, that.value); } @Override public int hashCode() { return Objects.hash(value); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnumCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.base; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DynamicEnumCache> { private static final Logger LOGGER = LoggerFactory.getLogger(DynamicEnumCache.class); private final Class cls; private final Map values; /** * * @param cls */ private final Constructor constructor; public DynamicEnumCache(Class cls) { try { this.cls = cls; this.constructor = initFactory(); this.values = initValues(); } catch (Exception e) { throw new IllegalStateException("failed to init dynamic enum, class=" + cls.getName(), e); } } private Constructor initFactory() throws NoSuchMethodException { ParameterizedType superClass = (ParameterizedType) cls.getGenericSuperclass(); Class argument = (Class) superClass.getActualTypeArguments()[0]; return cls.getConstructor(argument); } @SuppressWarnings("unchecked") private Map initValues() { Map values = new LinkedHashMap<>(); EnumUtils.findEnumFields(cls) .map(field -> (T) EnumUtils.readEnum(field)) .forEach(oneEnum -> values.put(oneEnum.getValue(), oneEnum)); return Collections.unmodifiableMap(values); } public T fromValue(Object value) { if (value == null) { return null; } T instance = values.get(value); if (instance != null) { return instance; } try { // do not cache unknown value to avoid attach // if need to cache unknown value, should avoid cache too many values instance = constructor.newInstance(value); instance.setDynamic(true); return instance; } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { LOGGER.error("failed to create enum, class={}, value={}.", cls.getName(), value); return null; } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/EnumUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.base; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.Arrays; import java.util.stream.Stream; public interface EnumUtils { @SuppressWarnings("unchecked") static T readEnum(Field enumField) { try { return (T) enumField.get(null); } catch (IllegalAccessException e) { throw new IllegalStateException("failed to read enum, field=" + enumField, e); } } static boolean isEnumField(Class cls, Field field) { return Modifier.isStatic(field.getModifiers()) && cls.equals(field.getType()); } static Stream findEnumFields(Class cls) { return Arrays.stream(cls.getFields()) .filter(field -> isEnumField(cls, field)); } static boolean isDynamicEnum(Type cls) { return cls instanceof DynamicEnum; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.base; public interface ServiceCombConstants { String CONFIG_TRACING_COLLECTOR_ADDRESS = "servicecomb.tracing.collector.address"; String CONFIG_TRACING_COLLECTOR_API_V1 = "v1"; String CONFIG_TRACING_COLLECTOR_API_V2 = "v2"; String CONFIG_TRACING_COLLECTOR_API_VERSION = "servicecomb.tracing.collector.apiVersion"; String CONFIG_TRACING_COLLECTOR_PATH = "/api/{0}/spans"; String DEFAULT_TRACING_COLLECTOR_ADDRESS = "http://127.0.0.1:9411"; String CONFIG_KEY_SPLITTER = "_"; String CONFIG_FRAMEWORK_DEFAULT_NAME = "servicecomb-java-chassis"; String CONFIG_DEFAULT_REGISTER_BY = "SDK"; String DEVELOPMENT_SERVICECOMB_ENV = "development"; String SERVICE_MAPPING = "SERVICE_MAPPING"; String VERSION_MAPPING = "VERSION_MAPPING"; String APP_MAPPING = "APP_MAPPING"; } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.cache; import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; @SuppressWarnings("unchecked") public class VersionedCache { protected static final AtomicInteger VERSION = new AtomicInteger(); interface IsEmpty { boolean isEmpty(); } protected volatile int cacheVersion; // an optional name protected String name; protected Object data; protected IsEmpty isEmpty = this::isCommonEmpty; private boolean isCommonEmpty() { return data == null; } private boolean isMapEmpty() { return ((Map) data).isEmpty(); } private boolean isCollectionEmpty() { return ((Collection) data).isEmpty(); } private boolean isArrayEmpty() { return ((Object[]) data).length == 0; } public int cacheVersion() { return cacheVersion; } public T autoCacheVersion() { this.cacheVersion = VERSION.incrementAndGet(); return (T) this; } public T cacheVersion(int cacheVersion) { this.cacheVersion = cacheVersion; return (T) this; } public String name() { return name; } public T name(String name) { this.name = name; return (T) this; } public T subName(VersionedCache parent, String subName) { Objects.requireNonNull(parent.name()); Objects.requireNonNull(subName); this.name = parent.name + "/" + subName; return (T) this; } public T data() { return (T) data; } // caller maker sure data is a map public Map mapData() { return (Map) data; } // caller maker sure data is a collection public Collection collectionData() { return (Collection) data; } //caller maker sure data is a array public T[] arrayData() { return (T[]) data; } public T data(Object data) { this.data = data; if (data == null) { isEmpty = this::isCommonEmpty; } else if (Map.class.isInstance(data)) { isEmpty = this::isMapEmpty; } else if (Collection.class.isInstance(data)) { isEmpty = this::isCollectionEmpty; } else if (data.getClass().isArray()) { isEmpty = this::isArrayEmpty; } else { isEmpty = this::isCommonEmpty; } return (T) this; } // only newData is new than this, means expired. public boolean isExpired(VersionedCache newCache) { // cacheVersion maybe overflow, so must use decrease to determine return newCache.cacheVersion - cacheVersion > 0; } public boolean isSameVersion(VersionedCache newCache) { return newCache.cacheVersion == cacheVersion; } public boolean isEmpty() { return isEmpty.isEmpty(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrency/SuppressedRunnableWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.concurrency; import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A wrapper for {@link Runnable}. * All the exceptions thrown from {@link Runnable#run()} are caught and handled. */ public class SuppressedRunnableWrapper implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(SuppressedRunnableWrapper.class); private final Runnable target; private final Consumer errorHandler; public SuppressedRunnableWrapper(Runnable runnable) { this(runnable, null); } public SuppressedRunnableWrapper(Runnable runnable, Consumer errorHandler) { this.target = runnable; this.errorHandler = errorHandler; } @Override public void run() { try { target.run(); } catch (Throwable e) { handleError(e); } } private void handleError(Throwable e) { if (null == errorHandler) { LOGGER.error("task {} execute error!", target, e); return; } errorHandler.accept(e); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrent/ConcurrentHashMapEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.concurrent; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; public class ConcurrentHashMapEx extends ConcurrentHashMap { private static final long serialVersionUID = -7753722464102569902L; public ConcurrentHashMapEx() { super(); } public ConcurrentHashMapEx(int initialCapacity) { super(initialCapacity); } public ConcurrentHashMapEx(Map m) { super(m); } public ConcurrentHashMapEx(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); } public ConcurrentHashMapEx(int initialCapacity, float loadFactor, int concurrencyLevel) { super(initialCapacity, loadFactor, concurrencyLevel); } // ConcurrentHashMap.computeIfAbsent always do "synchronized" operation // so we wrap it to improve performance @Override public V computeIfAbsent(K key, Function mappingFunction) { V value = get(key); if (value != null) { return value; } return super.computeIfAbsent(key, mappingFunction); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/ConfigLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config; public interface ConfigLoader { T load() throws Exception; } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.config.impl.PropertiesLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; public class PaaSResourceUtils extends org.springframework.util.ResourceUtils { public static final String PROPERTIES_SUFFIX = ".properties"; private static final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); /** * 失败,则返回空数组 */ public static Resource[] getResources(String locationPattern) { try { return resourcePatternResolver.getResources(locationPattern); } catch (IOException e) { return new Resource[0]; } } /** * 失败,则返回空列表 */ public static List getResources(String... locationPatterns) { List ret = new ArrayList<>(); for (String locationPattern : locationPatterns) { Resource[] resArr = getResources(locationPattern); ret.addAll(Arrays.asList(resArr)); } return ret; } /** * 文件同名时,jar的优先级比较低 * jar: xxx.xml * jar: xxx.model.xml * file:xxx.xml * 调用者保证,所有res的后缀都是suffix * file文件应该只有一个,因为放在目录中的配置文件,应该是最终的部署定制文件 * 此时,还分多个,是不合适的 */ public static void sortResources(List resList, String suffix) { resList.sort((o1, o2) -> { try { // jar的优先级比较低 if (isJarURL(o1.getURL()) && isFileURL(o2.getURL())) { return -1; } // 干掉后缀,再排序 String name1 = o1.getFilename(); String name2 = o2.getFilename(); //Resource.getFilename接口会返回null,当路径的文件名不存在时 //配置文件一定存在,并且Resource有合法的文件名,name1和name2不可能为空, //这里做判断是为了处理CodeDEX报的null引用 if (StringUtils.isEmpty(name1) || StringUtils.isEmpty(name2)) { throw new IOException( String.format("Resource %s or %s is not a file", o1.getURI(), o2.getURI())); } name1 = name1.substring(0, name1.length() - suffix.length()); name2 = name2.substring(0, name2.length() - suffix.length()); return name1.compareTo(name2); } catch (IOException e) { throw new RuntimeException(e); } }); } public static void sortProperties(List resList) { sortResources(resList, PROPERTIES_SUFFIX); } public static List getSortedResources(String locationPattern, String suffix) { if (StringUtils.isEmpty(locationPattern)) { throw new RuntimeException("Resource path must not be null or empty"); } if (!locationPattern.endsWith(suffix)) { throw new RuntimeException("Resource path must ends with " + suffix); } String prefix = locationPattern.substring(0, locationPattern.length() - suffix.length()); List resList = PaaSResourceUtils.getResources(locationPattern, prefix + ".*" + suffix); sortResources(resList, suffix); return resList; } public static List getSortedProperties(String locationPattern) { return getSortedResources(locationPattern, PROPERTIES_SUFFIX); } public static Properties loadMergedProperties(String locationPattern) throws Exception { PropertiesLoader loader = new PropertiesLoader(Arrays.asList(locationPattern)); return loader.load(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/AbstractLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config.impl; import java.util.List; import org.apache.servicecomb.foundation.common.config.ConfigLoader; public abstract class AbstractLoader implements ConfigLoader { protected List locationPatternList; public AbstractLoader(List locationPatternList) { this.locationPatternList = locationPatternList; } public List getLocationPatternList() { return locationPatternList; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config.impl; import java.io.IOException; import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.config.PaaSResourceUtils; import org.springframework.core.io.Resource; public class PaaSPropertiesLoaderUtils extends org.springframework.core.io.support.PropertiesLoaderUtils { /** * 使用内置规则 * 输入:/a/b/abc.properties * 实际: * 1./a/b/abc.properties * 2./a/b/abc.[ext].properties * @param locationPattern locationPattern * @return Properties * @throws IOException Exception */ public static Properties loadMergedProperties(String locationPattern) throws IOException { Properties prop = new Properties(); return fillMergedProperties(prop, locationPattern); } public static Properties fillMergedProperties(Properties prop, String locationPattern) throws IOException { if (StringUtils.isEmpty(locationPattern)) { throw new RuntimeException("Resource path must not be null or empty"); } String suffix = PaaSResourceUtils.PROPERTIES_SUFFIX; if (!locationPattern.endsWith(suffix)) { throw new RuntimeException("Resource path must ends with " + suffix); } String prefix = locationPattern.substring(0, locationPattern.length() - suffix.length()); List resList = PaaSResourceUtils.getResources(locationPattern, prefix + ".*" + suffix); PaaSResourceUtils.sortProperties(resList); fillAllProperties(prop, resList); return prop; } public static void fillAllProperties(Properties prop, List resList) throws IOException { for (Resource res : resList) { PaaSPropertiesLoaderUtils.fillProperties(prop, res); } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config.impl; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.apache.servicecomb.foundation.common.config.PaaSResourceUtils; import org.springframework.core.io.Resource; public class PropertiesLoader extends AbstractLoader { private final List foundResList = new ArrayList<>(); public PropertiesLoader(List locationPatternList) { super(locationPatternList); } public List getFoundResList() { return foundResList; } @SuppressWarnings("unchecked") @Override public T load() throws Exception { Properties props = new Properties(); for (String locationPattern : locationPatternList) { List resList = PaaSResourceUtils.getSortedProperties(locationPattern); foundResList.addAll(resList); PaaSPropertiesLoaderUtils.fillAllProperties(props, resList); } return (T) props; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config.impl; import java.io.IOException; import java.net.URL; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.servicecomb.foundation.common.utils.FortifyUtils; import org.springframework.core.io.Resource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; public final class XmlLoaderUtils { private XmlLoaderUtils() { } private static final ObjectMapper xmlMapper = new XmlMapper(); @SuppressWarnings("unchecked") public static T load(Resource res, Class cls) throws Exception { return (T) xmlMapper.readValue(res.getURL(), cls); } public static Document load(URL url) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory dbf = FortifyUtils.getSecurityXmlDocumentFactory(); // CodeDEX要求xml必须校验 // 不过这都是用于加载内部配置的,申请例外吧 dbf.setValidating(false); DocumentBuilder db = dbf.newDocumentBuilder(); return db.parse(url.toString()); } public static Document load(Resource res) throws Exception { return load(res.getURL()); } public static Document newDoc() throws ParserConfigurationException { DocumentBuilderFactory factory = FortifyUtils.getSecurityXmlDocumentFactory(); DocumentBuilder builder = factory.newDocumentBuilder(); return builder.newDocument(); } public static void mergeElement(Element from, Element to) { // attrs for (int idx = 0; idx < from.getAttributes().getLength(); idx++) { Node node = from.getAttributes().item(idx); to.getAttributes().setNamedItem(node.cloneNode(false)); } // children for (int idx = 0; idx < from.getChildNodes().getLength(); idx++) { Node node = from.getChildNodes().item(idx); if (!Element.class.isInstance(node)) { continue; } to.appendChild(node.cloneNode(true)); } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryption.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.encrypt; /** * Interface for users to encode/decode confidential data */ public interface Encryption { default int getOrder() { return 0; } /** * decode confidential data * @param encrypted encrypted data * @param tags extra information used to do something * @return plain data */ char[] decode(char[] encrypted, String tags); /** * * @param plain plain data * @param tags extra information used to do something * @return encrypted data */ char[] encode(char[] plain, String tags); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryptions.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.encrypt; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import com.google.common.annotations.VisibleForTesting; public class Encryptions { private static Encryption encryption = SPIServiceUtils.getPriorityHighestService(Encryption.class); @VisibleForTesting static void setEncryption(Encryption encryption) { Encryptions.encryption = encryption; } @VisibleForTesting static Encryption getEncryption() { return encryption; } public static String decode(String encrypted, String tags) { if (encrypted == null) { return null; } char[] result = decode(encrypted.toCharArray(), tags); if (result == null) { return null; } return new String(result); } public static char[] decode(char[] encrypted, String tags) { return encryption.decode(encrypted, tags); } public static String encode(String plain, String tags) { if (plain == null) { return null; } char[] result = encode(plain.toCharArray(), tags); if (result == null) { return null; } return new String(result); } public static char[] encode(char[] plain, String tags) { return encryption.encode(plain, tags); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/NoEncryption.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.encrypt; public class NoEncryption implements Encryption { @Override public int getOrder() { return 100; } @Override public char[] decode(char[] encrypted, String tags) { return encrypted; } @Override public char[] encode(char[] plain, String tags) { return plain; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/AlarmEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; public class AlarmEvent { Type type; public AlarmEvent(Type type) { this.type = type; } public Type getType() { return this.type; } public enum Type { OPEN, CLOSE } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EnableExceptionPropagation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * do not suppress exception */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface EnableExceptionPropagation { } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.common.eventbus.EventBus; /** * EventManager for chassis events * */ @Configuration public class EventManager { public static EventBus eventBus = new SimpleEventBus(); public static EventBus getEventBus() { return eventBus; } /** * Registering listener. */ public static void register(Object listener) { eventBus.register(listener); } /** * post event. */ public static void post(Object event) { eventBus.post(event); } /** * Unregistering listener. */ public static void unregister(Object listener) { eventBus.unregister(listener); } @Bean public EventBus scbEventBus() { return eventBus; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleEventBus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import org.apache.commons.lang3.reflect.MethodUtils; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; /** * for register/unregister rarely */ public class SimpleEventBus extends EventBus { private final Map> subscribersMap = new ConcurrentHashMapEx<>(); // key is event class private Map, List> subscribersCache = new ConcurrentHashMapEx<>(); private List collectSubscribers(Object instance) { List subscribers = new ArrayList<>(); Method[] methods = MethodUtils.getMethodsWithAnnotation(instance.getClass(), Subscribe.class, true, true); for (Method method : methods) { SimpleSubscriber subscriber = new SimpleSubscriber(instance, method); subscribers.add(subscriber); } return subscribers; } @Override public void register(Object instance) { subscribersMap.computeIfAbsent(instance, this::collectSubscribers); // even ignored cause of duplicate register // still reset cache // this makes logic simpler subscribersCache = new ConcurrentHashMapEx<>(); } @Override public void unregister(Object instance) { if (subscribersMap.remove(instance) != null) { subscribersCache = new ConcurrentHashMapEx<>(); } } @Override public void post(Object event) { // cache always reset after register/unregister // so cache always match latest subscribersMap at last // the worst scenes is invoke collectSubscriberForEvent multiple times, no problem List subscribers = subscribersCache .computeIfAbsent(event.getClass(), this::collectSubscriberForEvent); for (SimpleSubscriber subscriber : subscribers) { subscriber.dispatchEvent(event); } } /** * subscribersMap almost stable
* so we not care for performance of collectSubscriberForEvent * @param eventClass */ private List collectSubscriberForEvent(Class eventClass) { List subscribersForEvent = new ArrayList<>(); for (List subscribers : subscribersMap.values()) { for (SimpleSubscriber subscriber : subscribers) { if (subscriber.getMethod().getParameterTypes()[0].isAssignableFrom(eventClass)) { subscribersForEvent.add(subscriber); } } } subscribersForEvent.sort(Comparator.comparingInt(SimpleSubscriber::getOrder)); return subscribersForEvent; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleSubscriber.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.function.Consumer; import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.AllowConcurrentEvents; public class SimpleSubscriber { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSubscriber.class); private final Object instance; private final Method method; private int order; private final boolean enableExceptionPropagation; // generated from method private Consumer lambda; private Consumer dispatcher; public SimpleSubscriber(Object instance, Method method) { this.instance = instance; this.method = method; enableExceptionPropagation = method.getAnnotation(EnableExceptionPropagation.class) != null; SubscriberOrder subscriberOrder = method.getAnnotation(SubscriberOrder.class); if (subscriberOrder != null) { order = subscriberOrder.value(); } try { lambda = LambdaMetafactoryUtils.createLambda(instance, method, Consumer.class); } catch (Throwable throwable) { // because enhance LambdaMetafactoryUtils to support ALL_MODES by reflect // never run into this branch. // otherwise create a listener instance of anonymous class will run into this branch LOGGER.warn("Failed to create lambda for method: {}, fallback to reflect.", method, throwable); checkAccess(method); lambda = event -> { try { method.invoke(instance, event); } catch (Throwable e) { LOGGER.warn("Failed to call event listener {}.", method.getName()); throw new IllegalStateException(e); } }; } dispatcher = this::syncDispatch; if (method.getAnnotation(AllowConcurrentEvents.class) != null) { dispatcher = this::concurrentDispatch; } } private static void checkAccess(Method method) { // This check is not accurate. Most of time package visible and protected access can be ignored, so simply do this. if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { throw new IllegalStateException( String.format( "The event handler must be a public accessible method. NOTICE: " + "this is change from 2.0 and using higher version of JDK. " + "Declaring class is %s, method is %s", method.getDeclaringClass().getName(), method.getName())); } } public Object getInstance() { return instance; } public Method getMethod() { return method; } public int getOrder() { return order; } public void dispatchEvent(Object event) { try { dispatcher.accept(event); } catch (Throwable e) { if (enableExceptionPropagation) { throw e; } LOGGER.error("Event process should not throw exception when @EnableExceptionPropagation not set. ", e); } } private void syncDispatch(Object event) { synchronized (this) { lambda.accept(event); } } private void concurrentDispatch(Object event) { lambda.accept(event); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SubscriberOrder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * order of subscriber
* lower values will run first */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface SubscriberOrder { int value() default 0; } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/exceptions/ServiceCombException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.exceptions; public class ServiceCombException extends RuntimeException { private static final long serialVersionUID = -1085233183289520695L; public ServiceCombException(String cause, Throwable throwable) { super(cause, throwable); } public ServiceCombException(String cause) { super(cause); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status.Family; import jakarta.ws.rs.core.Response.StatusType; public class HttpStatus implements StatusType { public static boolean isSuccess(int code) { return Status.Family.SUCCESSFUL.equals(Status.Family.familyOf(code)); } public static boolean isSuccess(StatusType status) { return Status.Family.SUCCESSFUL.equals(status.getFamily()); } private final int statusCode; private final String reason; public HttpStatus(final int statusCode, final String reasonPhrase) { this.statusCode = statusCode; this.reason = reasonPhrase; } @Override public int getStatusCode() { return statusCode; } @Override public Family getFamily() { return Family.familyOf(statusCode); } @Override public String getReasonPhrase() { return reason; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; public class HttpStatusManager { private final Map statusMap = new ConcurrentHashMap<>(); public HttpStatusManager() { for (Status status : Status.values()) { statusMap.put(status.getStatusCode(), status); } SPIServiceUtils.getAllService(StatusType.class).forEach(this::addStatusType); } public void addStatusType(StatusType status) { if (statusMap.containsKey(status.getStatusCode())) { throw new IllegalStateException("repeated status code: " + status.getStatusCode()); } statusMap.put(status.getStatusCode(), status); } public StatusType getOrCreateByStatusCode(int code) { StatusType statusType = statusMap.get(code); if (statusType != null) { return statusType; } statusType = new HttpStatus(code, ""); addStatusType(statusType); return statusType; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import jakarta.ws.rs.core.Response.StatusType; public final class HttpStatusUtils { private static final HttpStatusManager MGR = new HttpStatusManager(); private HttpStatusUtils() { } public static StatusType getOrCreateByStatusCode(int code) { return MGR.getOrCreateByStatusCode(code); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import com.google.common.net.UrlEscapers; import org.apache.commons.lang3.StringUtils; public final class HttpUtils { private HttpUtils() { } /** * paramName is not case sensitive * @param headerValue example: attachment;filename=a.txt * */ public static String parseParamFromHeaderValue(String headerValue, String paramName) { if (StringUtils.isEmpty(headerValue)) { return null; } for (String value : headerValue.split(";")) { int idx = value.indexOf('='); if (idx == -1) { continue; } if (paramName.equalsIgnoreCase(value.substring(0, idx).trim())) { return value.substring(idx + 1).replaceAll("\"", "").trim(); } } return null; } /** *
   *          foo://example.com:8042/over/there?name=ferret#nose
   *          \_/   \______________/\_________/ \_________/ \__/
   *           |           |            |            |        |
   *        scheme     authority       path        query   fragment
   *           |   _____________________|__
   *          / \ /                        \
   *          urn:example:animal:ferret:nose
   * 
*

the URI syntax components above is referred from RFC3986. * This method is used to encode the entire path part(e.g. /over/there in the example).

* In order to keep the structure of path, slash '/' will not be encoded. If you want to encode '/' into {@code %2F}, * please consider the {@link #encodePathParam(String)} * * * @param path the entire url path * @return the encoded url path */ public static String uriEncodePath(String path) { try { URI uri = new URI(null, null, path, null); return uri.toASCIIString(); } catch (URISyntaxException e) { throw new IllegalArgumentException(String.format("uriEncode failed, path=\"%s\".", path), e); } } /** * Encode path params. For example, if the path of an operation is {@code /over/there/{pathParam}/tail}, this method * should be used to encoded {@code {pathParam}}. In order to keep the path structure, the slash '/' will be encoded * into {@code %2F} to avoid path matching problem. * * @see UrlEscapers#urlPathSegmentEscaper() * * @param pathParam the path param to be encoded * @return the encoded path param */ public static String encodePathParam(String pathParam) { return UrlEscapers.urlPathSegmentEscaper().escape(pathParam); } public static String uriDecodePath(String path) { if (path == null) { return null; } try { return new URI(path).getPath(); } catch (URISyntaxException e) { throw new IllegalArgumentException(String.format("uriDecode failed, path=\"%s\".", path), e); } } /** * only used by SDK to download from serviceComb producer
* no need to check rtf6266's "filename*" rule. */ public static String parseFileNameFromHeaderValue(String headerValue) { String fileName = parseParamFromHeaderValue(headerValue, "filename"); fileName = StringUtils.isEmpty(fileName) ? "default" : fileName; fileName = uriDecodePath(fileName); return new File(fileName).getName(); } /** * Parse the character encoding from the specified content type header. * If the content type is null, or there is no explicit character encoding, * null is returned. * * @param contentType a content type header */ public static String getCharsetFromContentType(String contentType) { if (contentType == null) { return null; } int start = contentType.indexOf("charset="); if (start < 0) { return null; } String encoding = contentType.substring(start + 8); int end = encoding.indexOf(';'); if (end >= 0) { encoding = encoding.substring(0, end); } encoding = encoding.trim(); if ((encoding.length() > 2) && (encoding.startsWith("\"")) && (encoding.endsWith("\""))) { encoding = encoding.substring(1, encoding.length() - 1); } return encoding.trim(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/io/AsyncCloseable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.io; import java.util.concurrent.CompletableFuture; public interface AsyncCloseable { CompletableFuture close(); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/log/AbstractMarker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.log; import java.util.Collections; import java.util.Iterator; import org.slf4j.Marker; public abstract class AbstractMarker implements Marker { private static final long serialVersionUID = -1L; @Override public void add(Marker reference) { } @Override public boolean remove(Marker reference) { return false; } @Deprecated @Override public boolean hasChildren() { return false; } @Override public boolean hasReferences() { return false; } @SuppressWarnings("unchecked") @Override public Iterator iterator() { return Collections.EMPTY_LIST.iterator(); } @Override public boolean contains(Marker other) { return false; } @Override public boolean contains(String name) { return false; } @Override public String toString() { return getName(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.net; import java.net.InetSocketAddress; import com.google.common.base.Objects; public class IpPort { private volatile String hostOrIp; private volatile int port; private volatile InetSocketAddress socketAddress; private final Object lock = new Object(); public IpPort() { } public IpPort(String hostOrIp, int port) { this.hostOrIp = hostOrIp; this.port = port; } public String getHostOrIp() { return hostOrIp; } public void setHostOrIp(String hostOrIp) { this.hostOrIp = hostOrIp; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } @Override public int hashCode() { return Objects.hashCode(port, hostOrIp); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } IpPort ipPort = (IpPort) o; if (port != ipPort.port) { return false; } return hostOrIp.equals(ipPort.hostOrIp); } @Override public String toString() { return hostOrIp + ":" + port; } public InetSocketAddress getSocketAddress() { if (socketAddress == null) { synchronized (lock) { if (socketAddress == null) { InetSocketAddress tmpSocketAddress = new InetSocketAddress(hostOrIp, port); socketAddress = tmpSocketAddress; } } } return socketAddress; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.net; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; public final class NetUtils { private static final Logger LOGGER = LoggerFactory.getLogger(NetUtils.class); private static final String IPV4_KEY = "_v4"; private static final String IPV6_KEY = "_v6"; private static final String PREFERRED_INTERFACE = "eth"; // one interface can bind to multiple address // we only save one ip for each interface name. // eg: // 1. eth0 -> ip1 ip2 // last data is eth0 -> ip2 // 2. eth0 -> ip1 // eth0:0 -> ip2 // eth0:1 -> ip3 // on interface name conflict, all data saved // key is network interface name and type private static Map allInterfaceAddresses = new HashMap<>(); private static String hostName; private static String hostAddress; private static String hostAddressIpv6; static { doGetHostNameAndHostAddress(); } private static void doGetHostNameAndHostAddress() { try { doGetAddressFromNetworkInterface(); // getLocalHost will throw exception in some docker image and sometimes will do a hostname lookup and time consuming InetAddress localHost = InetAddress.getLocalHost(); hostName = localHost.getHostName(); LOGGER.info("localhost hostName={}, hostAddress={}.", hostName, localHost.getHostAddress()); if (!isLocalAddress(localHost)) { if (Inet6Address.class.isInstance(localHost)) { hostAddressIpv6 = trimIpv6(localHost.getHostAddress()); hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost); LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6); return; } hostAddress = localHost.getHostAddress(); hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost)); LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6); return; } hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost)); hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost); LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6); } catch (Exception e) { LOGGER.error("got exception when trying to get addresses:", e); if (allInterfaceAddresses.size() >= 1) { InetAddress entry = allInterfaceAddresses.entrySet().iterator().next().getValue(); // get host name will do a reverse name lookup and is time consuming hostName = entry.getHostName(); hostAddress = entry.getHostAddress(); LOGGER.info("add host name from interfaces:" + hostName + ",host address:" + hostAddress); } } } private static String tryGetHostAddressFromNetworkInterface(boolean isIpv6, InetAddress localhost) { InetAddress result = null; for (Entry entry : allInterfaceAddresses.entrySet()) { if (isIpv6 && entry.getKey().endsWith(IPV6_KEY)) { result = entry.getValue(); if (entry.getKey().startsWith(PREFERRED_INTERFACE)) { return result.getHostAddress(); } } else if (!isIpv6 && entry.getKey().endsWith(IPV4_KEY)) { result = entry.getValue(); if (entry.getKey().startsWith(PREFERRED_INTERFACE)) { return result.getHostAddress(); } } } if (result == null) { return localhost.getHostAddress(); } return result.getHostAddress(); } private NetUtils() { } /** * docker环境中,有时无法通过InetAddress.getLocalHost()获取 ,会报unknown host Exception, system error * 此时,通过遍历网卡接口的方式规避,出来的数据不一定对 */ private static void doGetAddressFromNetworkInterface() throws SocketException { Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface network = networkInterfaces.nextElement(); if (!network.isUp() || network.isLoopback() || network.isVirtual()) { continue; } Enumeration addresses = network.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (isLocalAddress(address)) { continue; } if (address instanceof Inet4Address) { LOGGER.info("add ipv4 network interface:" + network.getName() + ",host address:" + address.getHostAddress()); allInterfaceAddresses.put(network.getName() + IPV4_KEY, address); } else if (address instanceof Inet6Address) { LOGGER.info("add ipv6 network interface:" + network.getName() + ",host address:" + address.getHostAddress()); allInterfaceAddresses.put(network.getName() + IPV6_KEY, address); } } } } private static String trimIpv6(String hostAddress) { int index = hostAddress.indexOf("%"); if (index >= 0) { return hostAddress.substring(0, index); } return hostAddress; } private static boolean isLocalAddress(InetAddress address) { return address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isMulticastAddress(); } /** * The format of address should be {@code IPv4:port} or {@code [IPv6]:port}, or {@code host:port}, * or you will not get expected result. * * Note that the IPv6 address should be wrapped by square brackets. * @return IpPort parsed from input param, or {@code null} if the param is null. */ public static IpPort parseIpPort(String address) { if (address == null) { return null; } URI uri = URI.create("http://" + address); return parseIpPort(uri, true); } /** * Parse a {@link URI} into an {@link IpPort}. * *

* A uri without port is allowed, in which case the port will be inferred from the scheme. {@code http} is 80, and * {@code https} is 443. *

*

* The host of the {@code uri} should not be null, or it will be treated as an illegal param, * and an {@link IllegalArgumentException} will be thrown. *

*/ public static IpPort parseIpPort(URI uri) { return parseIpPort(uri, false); } /** * Parse a {@link URI} into an {@link IpPort} * @param uri a uri representing {@link IpPort} * @param ignorePortUndefined whether the port should be inferred from scheme, when the port part of {@code uri} is {@code -1}. * If {@code true} the undefined port is ignored; * otherwise a port will be inferred from scheme: {@code http} is 80, and {@code https} is 443. */ public static IpPort parseIpPort(URI uri, boolean ignorePortUndefined) { if (null == uri.getHost()) { // if the format of address is legal but the value is out of range, URI#create(String) will not throw exception // but return a URI with null host. throw new IllegalArgumentException("Illegal uri: [" + uri + "]"); } IpPort ipPort = new IpPort(uri.getHost(), uri.getPort()); if (-1 != ipPort.getPort() || ignorePortUndefined) { return ipPort; } if (uri.getScheme().equals("http")) { ipPort.setPort(80); } if (uri.getScheme().equals("https")) { ipPort.setPort(443); } return ipPort; } /** * @param uriAddress the address containing IP and port info. * @return IpPort parsed from input param, or {@code null} if the param is null. */ public static IpPort parseIpPortFromURI(String uriAddress) { if (uriAddress == null) { return null; } try { return parseIpPort(new URI(uriAddress)); } catch (URISyntaxException e) { return null; } } public static IpPort parseIpPort(String scheme, String authority) { if (authority == null) { return null; } return parseIpPort(URI.create(scheme + "://" + authority)); } /** * 对于配置为0.0.0.0的地址,let it go * schema, e.g. http * address, e.g 0.0.0.0:8080 * return 实际监听的地址 */ public static String getRealListenAddress(String schema, String address) { if (address == null) { return null; } try { URI originalURI = new URI(schema + "://" + address); // validate original url NetUtils.parseIpPort(originalURI); return originalURI.toString(); } catch (URISyntaxException e) { LOGGER.error("address {} is not valid.", address); return null; } } public static String getHostName() { //If failed to get host name ,micro-service will registry failed //So I add retry mechanism if (hostName == null) { doGetHostNameAndHostAddress(); } return hostName; } @VisibleForTesting static void resetHostName() { hostName = null; } public static String getHostAddress() { //If failed to get host address ,micro-service will registry failed //So I add retry mechanism if (hostAddress == null) { doGetHostNameAndHostAddress(); } return hostAddress; } public static String getIpv6HostAddress() { //If failed to get host address ,micro-service will registry failed //So I add retry mechanism if (hostAddressIpv6 == null) { doGetHostNameAndHostAddress(); } return hostAddressIpv6; } public static InetAddress getInterfaceAddress(String interfaceName) { return allInterfaceAddresses.get(interfaceName); } public static InetAddress ensureGetInterfaceAddress(String interfaceName) { InetAddress address = allInterfaceAddresses.get(interfaceName); if (address == null) { throw new IllegalArgumentException("Can not find address for interface name: " + interfaceName); } return address; } @SuppressWarnings({"unused", "try"}) public static boolean canTcpListen(InetAddress address, int port) { try (ServerSocket ss = new ServerSocket(port, 0, address)) { return true; } catch (IOException e) { return false; } } public static String humanReadableBytes(long bytes) { int unit = 1024; if (bytes < unit) { return String.valueOf(bytes); } int exp = (int) (Math.log(bytes) / Math.log(unit)); char pre = "KMGTPE".charAt(exp - 1); return String.format("%.3f%c", bytes / Math.pow(unit, exp), pre); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.net; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; /** * transport公共的Endpoint Object,当transport使用URI表示的时候,可以转化为这个对象。 */ public class URIEndpointObject extends IpPort { private static final String SSL_ENABLED_KEY = "sslEnabled"; private static final String PROTOCOL_KEY = "protocol"; private static final String WEBSOCKET_ENABLED_KEY = "websocketEnabled"; private static final String HTTP2 = "http2"; private final boolean sslEnabled; private boolean http2Enabled; private boolean websocketEnabled; private final Map> queries; private final String schema; public URIEndpointObject(String endpoint) { URI uri = URI.create(endpoint); schema = uri.getScheme(); setHostOrIp(uri.getHost()); if (uri.getPort() < 0) { // do not use default port throw new IllegalArgumentException("port not specified."); } setPort(uri.getPort()); queries = splitQuery(uri); sslEnabled = Boolean.parseBoolean(getFirst(SSL_ENABLED_KEY)); websocketEnabled = Boolean.parseBoolean(getFirst(WEBSOCKET_ENABLED_KEY)); String httpVersion = getFirst(PROTOCOL_KEY); if (HTTP2.equals(httpVersion)) { http2Enabled = true; } } public static Map> splitQuery(URI uri) { final Map> queryPairs = new LinkedHashMap<>(); List pairs = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8); for (NameValuePair pair : pairs) { List list = queryPairs.computeIfAbsent(pair.getName(), name -> new ArrayList<>()); list.add(pair.getValue()); } return queryPairs; } public boolean isSslEnabled() { return sslEnabled; } public boolean isWebsocketEnabled() { return websocketEnabled; } public boolean isHttp2Enabled() { return http2Enabled; } public String getSchema() { return this.schema; } public List getQuery(String key) { return queries.get(key); } public String getFirst(String key) { List values = queries.get(key); // it's impossible that values is not null and size is 0 if (values == null) { return null; } return values.get(0); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/AbstractPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import javax.activation.MimetypesFileTypeMap; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.MediaType; public class AbstractPart implements Part { private static final MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); protected String name; private String submittedFileName; protected String contentType; @Override public InputStream getInputStream() throws IOException { throw new Error("not supported method"); } @Override public String getContentType() { return contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM; } @SuppressWarnings("unchecked") public T contentType(String contentType) { this.contentType = contentType; return (T) this; } @Override public String getName() { return name; } @Override public String getSubmittedFileName() { return submittedFileName; } public AbstractPart setSubmittedFileName(String submittedFileName) { this.submittedFileName = submittedFileName; updateContentType(); return this; } private void updateContentType() { if (contentType != null || submittedFileName == null) { return; } contentType = mimetypesFileTypeMap.getContentType(submittedFileName); } @Override public long getSize() { throw new Error("not supported method"); } @Override public void write(String fileName) throws IOException { throw new Error("not supported method"); } @Override public void delete() throws IOException { throw new Error("not supported method"); } @Override public String getHeader(String name) { throw new Error("not supported method"); } @Override public Collection getHeaders(String name) { throw new Error("not supported method"); } @Override public Collection getHeaderNames() { throw new Error("not supported method"); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import org.apache.commons.io.FileUtils; public class FilePart extends AbstractPart implements FilePartForSend { private final File file; private boolean deleteAfterFinished; public FilePart(String name, String file) { this(name, new File(file)); } public FilePart(String name, File file) { this.name = name; this.file = file; this.setSubmittedFileName(this.file.getName()); } @Override public InputStream getInputStream() throws IOException { return Files.newInputStream(file.toPath()); } @Override public long getSize() { return file.length(); } @Override public void write(String fileName) throws IOException { FileUtils.copyFile(file, new File(fileName)); } @Override public void delete() throws IOException { file.delete(); } @Override public boolean isDeleteAfterFinished() { return deleteAfterFinished; } public FilePart setDeleteAfterFinished(boolean deleteAfterFinished) { this.deleteAfterFinished = deleteAfterFinished; return this; } @Override public String getAbsolutePath() { return file.getAbsolutePath(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePartForSend.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import jakarta.servlet.http.Part; public interface FilePartForSend extends Part { boolean isDeleteAfterFinished(); String getAbsolutePath(); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/InputStreamPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.InputStream; import org.springframework.core.io.InputStreamResource; public class InputStreamPart extends ResourcePart { public InputStreamPart(String name, InputStream inputStream) { super(name, new InputStreamResource(inputStream)); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/ResourcePart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.IOException; import java.io.InputStream; import org.springframework.core.io.Resource; public class ResourcePart extends AbstractPart { private final Resource resource; public ResourcePart(String name, Resource resource) { this.name = name; this.resource = resource; this.setSubmittedFileName(resource.getFilename()); } @Override public InputStream getInputStream() throws IOException { return resource.getInputStream(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/spring/PaasNamespaceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.spring; import java.util.Map.Entry; import java.util.Properties; import org.apache.servicecomb.foundation.common.config.PaaSResourceUtils; import org.apache.servicecomb.foundation.common.utils.FortifyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; //根据各个jar提供的信息来注册parser public class PaasNamespaceHandler extends NamespaceHandlerSupport { private static final Logger LOGGER = LoggerFactory.getLogger(PaasNamespaceHandler.class); private static final String NAMESPACE_RES = "classpath*:META-INF/spring/namespace.properties"; // @Override public void init() { Properties properties = null; try { properties = PaaSResourceUtils.loadMergedProperties(NAMESPACE_RES); } catch (Exception e) { LOGGER.error("Failed to load namespace handler define, {}, {}", NAMESPACE_RES, FortifyUtils.getErrorInfo(e)); return; } for (Entry entry : properties.entrySet()) { String className = entry.getValue().toString(); try { Class clazz = Class.forName(className); Object instance = clazz.getDeclaredConstructor().newInstance(); registerBeanDefinitionParser(entry.getKey().toString(), (BeanDefinitionParser) instance); } catch (ReflectiveOperationException e) { // 类找不到,说明没部署相应的jar包,这不一定是错误 // 可能是业务就选择不部署相应的jar包 // 所以只是打印个info日志 LOGGER.info("Failed to create BeanDefinitionParser instance of {}", className); } } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AbstractRestObjectMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import com.fasterxml.jackson.databind.ObjectMapper; public abstract class AbstractRestObjectMapper extends ObjectMapper { private static final long serialVersionUID = 189026839992490564L; public AbstractRestObjectMapper() { super(); } public AbstractRestObjectMapper(RestObjectMapper src) { super(src); } public abstract String convertToString(Object value) throws Exception; } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AsyncUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; public final class AsyncUtils { private AsyncUtils() { } public static CompletableFuture tryCatchSupplier(Supplier supplier) { try { T value = supplier.get(); return CompletableFuture.completedFuture(value); } catch (Throwable e) { return completeExceptionally(e); } } public static CompletableFuture tryCatchSupplierFuture(Supplier> supplier) { try { return supplier.get(); } catch (Throwable e) { return completeExceptionally(e); } } public static CompletableFuture completeExceptionally(Throwable throwable) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(throwable); return future; } /** * throws {@code exception} as RuntimeException. * * @param exception exception which will be rethrow */ public static RuntimeException rethrow(Throwable exception) { if (exception instanceof RuntimeException) { return (RuntimeException) exception; } return new ServiceCombException("Not declared exception", exception); } public static T toSync(CompletableFuture future) { try { return future.get(); } catch (ExecutionException executionException) { throw rethrow(executionException.getCause()); } catch (Throwable e) { throw rethrow(e); } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.context.ApplicationContext; public final class BeanUtils { private static ApplicationContext context; private BeanUtils() { } public static ApplicationContext getContext() { return context; } public static void setContext(ApplicationContext applicationContext) { context = applicationContext; } /** * 不应该在业务流程中频繁调用,因为内部必然会加一个锁做互斥,会影响并发度 */ @SuppressWarnings("unchecked") public static T getBean(String name) { return (T) context.getBean(name); } public static Map getBeansOfType(Class type) { if (context == null) { // for some test case return Collections.emptyMap(); } return context.getBeansOfType(type); } public static T getBean(Class type) { if (context == null) { // for some test case return null; } return context.getBean(type); } /** * Get the implemented class of the given instance * @param bean the instance to get implemented class from * @return the implemented class (if the checked class is proxied, return the ultimate target class) * @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass */ public static Class getImplClassFromBean(Object bean) { return AopProxyUtils.ultimateTargetClass(bean); } public static void addBeans(Class cls, List exists) { if (context == null) { return; } for (T instance : exists) { context.getAutowireCapableBeanFactory().autowireBean(instance); } for (T bean : context.getBeansOfType(cls).values()) { if (bean.enabled()) { exists.add(bean); } } exists.sort(Comparator.comparingInt(SPIOrder::getOrder)); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ClassLoaderScopeContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.HashMap; import java.util.Map; import com.google.common.annotations.VisibleForTesting; /** * class loader scope property is used when users run java-chassis in an class loader separated environment. * * For examples, deploy two war's to web container, or deploy two bundles in an OSGI container. * * Now java chassis not testing this feature carefully, but we will support users doing so. * * users who using this feature can feed back your problems in issues. * */ public class ClassLoaderScopeContext { private static final Map CLASS_LOADER_SCOPE_CONTEXT = new HashMap<>(); public static void setClassLoaderScopeProperty(String key, String value) { CLASS_LOADER_SCOPE_CONTEXT.put(key, value); } public static String getClassLoaderScopeProperty(String key) { return CLASS_LOADER_SCOPE_CONTEXT.get(key); } @VisibleForTesting public static void clearClassLoaderScopeProperty() { CLASS_LOADER_SCOPE_CONTEXT.clear(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; public class ConditionWaiter { private final AtomicReference dataReference; private final AtomicBoolean isComplete; private final long sleepDuration; private final TimeUnit timeUnit; private final ExecutorService executorService; public ConditionWaiter(T initialData, long sleepDuration, TimeUnit timeUnit) { this.dataReference = new AtomicReference<>(initialData); this.isComplete = new AtomicBoolean(false); this.sleepDuration = sleepDuration; this.timeUnit = timeUnit; this.executorService = Executors.newSingleThreadExecutor(); } public T waitForCompletion() { while (!isComplete.get()) { SleepUtil.sleep(sleepDuration, timeUnit); } return dataReference.get(); } public void setData(T newData) { dataReference.set(newData); } public void executeTaskAsync(Callable task) { CompletableFuture.supplyAsync(() -> { try { return task.call(); } catch (Exception e) { throw new RuntimeException("Task execution failed", e); } }, executorService).thenAccept(result -> { setData(result); isComplete.set(true); }); } public static class SleepUtil { public static void sleep(long duration, TimeUnit timeUnit) { try { timeUnit.sleep(duration); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("Thread was interrupted during sleep!"); } } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ExceptionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public class ExceptionUtils { private static final int DEPTH = 5; private ExceptionUtils() { } public static String getExceptionMessageWithoutTrace(Throwable e) { StringBuilder result = new StringBuilder(); appendExceptionInfo(e, result); Throwable cause = e.getCause(); int depth = DEPTH; while (cause != null && depth-- > 0) { result.append(";"); appendExceptionInfo(cause, result); cause = cause.getCause(); } return result.toString(); } private static void appendExceptionInfo(Throwable e, StringBuilder result) { result.append("cause:"); result.append(e.getClass().getSimpleName()); result.append(",message:"); result.append(e.getMessage()); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FilePerm.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.attribute.AclEntryPermission; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Arrays; import java.util.EnumSet; import java.util.Set; public final class FilePerm { private FilePerm() { } /** * owner 可读 */ public static final int FILE_PERM_UREAD = 256; /** * owner 可写 */ public static final int FILE_PERM_UWRITE = 128; /** * owner 可执行 */ public static final int FILE_PERM_UEXEC = 64; /** * 同组可读 */ public static final int FILE_PERM_GREAD = 32; /** * 同组可写 */ public static final int FILE_PERM_GWRITE = 16; /** * 同组可执行 */ public static final int FILE_PERM_GEXEC = 8; /** * 其他可读 */ public static final int FILE_PERM_OREAD = 4; /** * 其他可写 */ public static final int FILE_PERM_OWRITE = 2; /** * 其他可执行 */ public static final int FILE_PERM_OEXEC = 1; /** * mask */ public static final int FILE_PERM_MASK = 511; private static final AclEntryPermission[] permList = new AclEntryPermission[] { AclEntryPermission.READ_DATA, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_NAMED_ATTRS, AclEntryPermission.READ_ACL, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA, AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_NAMED_ATTRS, AclEntryPermission.WRITE_ACL, AclEntryPermission.SYNCHRONIZE }; /** * 获取默认Posix权限:640 */ public static Set getDefaultPosixPerm() { return PosixFilePermissions.fromString("rw-r-----"); } /** * 获取Posix权限 */ public static Set getPosixPerm(int perm) { StringBuilder permStr = new StringBuilder(); permStr.append(uCanRead(perm) ? "r" : "-"); permStr.append(uCanWrite(perm) ? "w" : "-"); permStr.append(uCanExec(perm) ? "x" : "-"); permStr.append(gCanRead(perm) ? "r" : "-"); permStr.append(gCanWrite(perm) ? "w" : "-"); permStr.append(gCanExec(perm) ? "x" : "-"); permStr.append(oCanRead(perm) ? "r" : "-"); permStr.append(oCanWrite(perm) ? "w" : "-"); permStr.append(oCanExec(perm) ? "x" : "-"); return PosixFilePermissions.fromString(permStr.toString()); } /** * 获取默认acl权限 */ public static Set getDefaultAclPerm() { Set perms = EnumSet.noneOf(AclEntryPermission.class); perms.addAll(Arrays.asList(permList)); return perms; } /** * owner是否可读 */ public static boolean uCanRead(int perm) { return (FILE_PERM_UREAD & perm) > 0; } /** * owner是否可写 */ public static boolean uCanWrite(int perm) { return (FILE_PERM_UWRITE & perm) > 0; } /** * owner是否可执行 */ public static boolean uCanExec(int perm) { return (FILE_PERM_UEXEC & perm) > 0; } /** * 同组是否可读 */ public static boolean gCanRead(int perm) { return (FILE_PERM_GREAD & perm) > 0; } /** * 同组是否可写 */ public static boolean gCanWrite(int perm) { return (FILE_PERM_GWRITE & perm) > 0; } /** * 同组是否可执行 */ public static boolean gCanExec(int perm) { return (FILE_PERM_GEXEC & perm) > 0; } /** * 其他是否可读 */ public static boolean oCanRead(int perm) { return (FILE_PERM_GREAD & perm) > 0; } /** * 其他是否可写 */ public static boolean oCanWrite(int perm) { return (FILE_PERM_GWRITE & perm) > 0; } /** * 其他是否可执行 */ public static boolean oCanExec(int perm) { return (FILE_PERM_GEXEC & perm) > 0; } /** * 设置文件权限。前提:必须支持PosixFileAttributeView. */ public static void setFilePerm(File file, String perm) { if (filePermSupported()) { try { Set perms = PosixFilePermissions.fromString(perm); PosixFileAttributes attr = Files.readAttributes(file.toPath(), PosixFileAttributes.class); attr.permissions().clear(); Files.setPosixFilePermissions(file.toPath(), perms); } catch (IOException e) { throw new IllegalStateException(e); } } } public static boolean filePermSupported() { FileSystem system = FileSystems.getDefault(); for (String name : system.supportedFileAttributeViews()) { // see javadoc for PosixFileAttributeView. if ("posix".equals(name)) { return true; } } return false; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FortifyUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; /** * 规避fortify问题,仅仅是规避,如 * e.getMessage * e.printStackTrace * 调用会报安全问题(敏感信息泄露) * * */ public final class FortifyUtils { private static final Method getMessageMethod; private static final Method printStackTraceMethod; static { try { getMessageMethod = Throwable.class.getMethod("getMessage"); printStackTraceMethod = Throwable.class.getMethod("printStackTrace", PrintWriter.class); } catch (Exception e) { throw new Error(e); } } private FortifyUtils() { } public static String getErrorMsg(Throwable e) { if (e == null) { return ""; } try { return (String) getMessageMethod.invoke(e); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { return ""; } } public static String getErrorStack(Throwable e) { if (null == e) { return ""; } try { StringWriter errors = new StringWriter(); printStackTraceMethod.invoke(e, new PrintWriter(errors)); return errors.toString(); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { return ""; } } public static String getErrorInfo(Throwable e) { return getErrorInfo(e, true); } public static String getErrorInfo(Throwable e, boolean isPrintMsg) { StringBuilder error = new StringBuilder(System.lineSeparator()); error.append("Exception: ").append(e.getClass().getName()).append("; "); if (isPrintMsg) { error.append(getErrorMsg(e)).append(System.lineSeparator()); } error.append(getErrorStack(e)); return error.toString(); } public static DocumentBuilderFactory getSecurityXmlDocumentFactory() throws ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); factory.setValidating(true); return factory; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public final class GenericsUtils { /** * check if XXX.class is generic type. see TestGenericsUtils for details meaning. * This method is provided for API compatibility for RestTemplate. Following code: *

*{@code * List>> response = consumers.getSCBRestTemplate() * postForObject("/testListObjectParam", request, List.class); * } * * should work for versions of 1.*. This is because java-chassis can read type info from swaggers. *

* Start from 2.x, the best practise to write this code is to use ParameterizedTypeReference provided by RestTemplate * exchange method. */ public static boolean isGenericResponseType(Type type) { if (type instanceof ParameterizedType) { return false; } if (type instanceof Class) { return ((Class) type).getTypeParameters().length > 0; } return true; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/IOUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.Closeable; import org.apache.commons.lang3.StringUtils; public class IOUtils { @SuppressWarnings("deprecation") public static void closeQuietly(final Closeable closeable) { org.apache.commons.io.IOUtils.closeQuietly(closeable); } public static String anonymousPath(String path) { if (StringUtils.isEmpty(path)) { return path; } StringBuilder result = new StringBuilder(); char separator = path.contains("/") ? '/' : '\\'; char[] tokens = path.toCharArray(); boolean byPass = false; boolean fileName = true; for (int i = tokens.length - 1; i >= 0; i--) { if (tokens[i] == separator) { fileName = false; byPass = false; result.append(separator); continue; } if (!byPass || fileName) { result.append(tokens[i]); byPass = true; } } return result.reverse().toString(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JsonUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; public final class JsonUtils { public static final ObjectMapper OBJ_MAPPER; private static final ObjectMapper UNICODE_OBJ_MAPPER; static { OBJ_MAPPER = new RestObjectMapper(); UNICODE_OBJ_MAPPER = OBJ_MAPPER.copy(); UNICODE_OBJ_MAPPER.enable(JsonWriteFeature.ESCAPE_NON_ASCII.mappedFeature()); } private JsonUtils() { } public static T readValue(byte[] src, Class valueType) throws IOException { return OBJ_MAPPER.readValue(src, valueType); } public static T readValue(InputStream is, Class valueType) throws IOException { return OBJ_MAPPER.readValue(is, valueType); } public static T readValue(InputStream is, JavaType valueType) throws IOException { return OBJ_MAPPER.readValue(is, valueType); } public static byte[] writeValueAsBytes(Object value) throws JsonProcessingException { return OBJ_MAPPER.writeValueAsBytes(value); } public static String writeValueAsString(Object value) throws JsonProcessingException { return OBJ_MAPPER.writeValueAsString(value); } public static String writeUnicodeValueAsString(Object value) throws JsonProcessingException { return UNICODE_OBJ_MAPPER.writeValueAsString(value); } public static T convertValue(Object fromValue, Class toValueType) { return OBJ_MAPPER.convertValue(fromValue, toValueType); } public static T convertValue(Object fromValue, JavaType toValueType) { return OBJ_MAPPER.convertValue(fromValue, toValueType); } public static void writeValue(OutputStream out, Object value) throws IOException { OBJ_MAPPER.writeValue(out, value); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.File; import java.io.InputStream; import java.net.URL; import java.util.jar.JarFile; import java.util.jar.Manifest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; public final class JvmUtils { private static final Logger LOGGER = LoggerFactory.getLogger(JvmUtils.class); // available for oracle jdk/ open jdk, and maybe others @VisibleForTesting static final String SUN_JAVA_COMMAND = "sun.java.command"; private JvmUtils() { } /** * * @return main class or null, never throw exception. * Note that this method does not ensure that the scbMainClass can be returned correctly in the some scene. */ public static Class findMainClassByStackTrace() { String mainClass = null; StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); if (stackTrace != null && stackTrace.length > 0) { for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { mainClass = stackTraceElement.getClassName(); break; } } } if (StringUtils.isEmpty(mainClass)) { LOGGER.info("Can't found main class by stackTrace."); return null; } try { Class cls = Class.forName(mainClass); LOGGER.info("Found main class \"{}\" by stackTrace.", mainClass); return cls; } catch (Throwable e) { LOGGER.warn("\"{}\" is not a valid class.", mainClass, e); return null; } } /** * * @return main class or null, never throw exception. * Note that this method does not ensure that the scbMainClass can be returned correctly in the some scene,like "mvn spring-boot:run". */ public static Class findMainClass() { //Get the mainClass from the call stack String mainClass = null; // 1.run with java -cp ...... // command is main class and args // 2.run with java -jar ...... // command is jar file name and args String command = System.getProperty(SUN_JAVA_COMMAND); if (StringUtils.isNotEmpty(command)) { String mainClassOrJar = command.trim().split(" ")[0]; mainClass = readFromJar(mainClassOrJar); } if (StringUtils.isEmpty(mainClass)) { LOGGER.info("Can't found main class by manifest."); return null; } try { Class cls = Class.forName(mainClass); LOGGER.info("Found main class \"{}\".", mainClass); return cls; } catch (Throwable e) { LOGGER.warn("\"{}\" is not a valid class.", mainClass, e); return null; } } private static String readFromJar(String mainClassOrJar) { if (!mainClassOrJar.endsWith(".jar")) { return mainClassOrJar; } String manifestUri = "jar:file:" + new File(mainClassOrJar).getAbsolutePath() + "!/" + JarFile.MANIFEST_NAME; try { URL url = new URL(manifestUri); try (InputStream inputStream = url.openStream()) { Manifest manifest = new Manifest(inputStream); String startClass = manifest.getMainAttributes().getValue("Start-Class"); if (StringUtils.isNotEmpty(startClass)) { return startClass; } return manifest.getMainAttributes().getValue("Main-Class"); } } catch (Throwable e) { LOGGER.warn("Failed to read Main-Class from \"{}\".", manifestUri, e); return null; } } /** * find a property class loader to avoid null */ public static ClassLoader correctClassLoader(ClassLoader classLoader) { ClassLoader targetClassLoader = classLoader; if (targetClassLoader == null) { targetClassLoader = Thread.currentThread().getContextClassLoader(); } if (targetClassLoader == null) { targetClassLoader = JvmUtils.class.getClassLoader(); } return targetClassLoader; } public static ClassLoader findClassLoader() { return correctClassLoader(null); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.security.PrivateKey; import java.security.PublicKey; public final class KeyPairEntry { private final PrivateKey privateKey; private final PublicKey publicKey; private final String publicKeyEncoded; public KeyPairEntry(PrivateKey privateKey, PublicKey publicKey, String publicKeyEncoded) { this.privateKey = privateKey; this.publicKey = publicKey; this.publicKeyEncoded = publicKeyEncoded; } public PrivateKey getPrivateKey() { return privateKey; } public PublicKey getPublicKey() { return publicKey; } public String getPublicKeyEncoded() { return publicKeyEncoded; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KeyPairUtils { private static final Logger LOGGER = LoggerFactory.getLogger(KeyPairUtils.class); private static final String KEY_GENERATOR_ALGORITHM = LegacyPropertyFactory.getStringProperty( "servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA"); private static final String SIGN_ALG = LegacyPropertyFactory.getStringProperty( "servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA"); private static final int KEY_SIZE = LegacyPropertyFactory.getIntProperty( "servicecomb.publicKey.accessControl.keySize", 2048); private static final Base64.Encoder encoder = Base64.getEncoder(); private static final Base64.Decoder decoder = Base64.getDecoder(); private static KeyFactory kf = null; static { try { kf = KeyFactory.getInstance(KEY_GENERATOR_ALGORITHM); } catch (NoSuchAlgorithmException e) { LOGGER.error("init keyfactory error"); } } public static KeyPairEntry generateALGKeyPair() { try { KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(KEY_GENERATOR_ALGORITHM); keyGenerator.initialize(KEY_SIZE, new SecureRandom()); KeyPair keyPair = keyGenerator.generateKeyPair(); PublicKey pubKey = keyPair.getPublic(); PrivateKey privKey = keyPair.getPrivate(); return new KeyPairEntry(privKey, pubKey, encoder.encodeToString(pubKey.getEncoded())); } catch (NoSuchAlgorithmException e) { LOGGER.error("generate rsa keypair failed"); throw new IllegalStateException("perhaps error occurred on jre"); } } /** * if has performance problem ,change Signature to ThreadLocal instance */ public static String sign(String content, PrivateKey privateKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { Signature signature = Signature.getInstance(SIGN_ALG); signature.initSign(privateKey); signature.update(content.getBytes(StandardCharsets.UTF_8)); byte[] signByte = signature.sign(); return encoder.encodeToString(signByte); } /** * * if has performance problem ,change Signature to ThreadLocal instance * @param publicKey public key after base64 encode * @param sign 签名 * @param content original content * @return verify result * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws SignatureException */ public static boolean verify(String publicKey, String sign, String content) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { if (null == kf) { throw new NoSuchAlgorithmException(KEY_GENERATOR_ALGORITHM + " KeyFactory not available"); } byte[] bytes = decoder.decode(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); PublicKey pubKey = kf.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGN_ALG); signature.initVerify(pubKey); signature.update(content.getBytes(StandardCharsets.UTF_8)); return signature.verify(decoder.decode(sign)); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaMetafactoryUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.bean.BoolGetter; import org.apache.servicecomb.foundation.common.utils.bean.BoolSetter; import org.apache.servicecomb.foundation.common.utils.bean.ByteGetter; import org.apache.servicecomb.foundation.common.utils.bean.ByteSetter; import org.apache.servicecomb.foundation.common.utils.bean.CharGetter; import org.apache.servicecomb.foundation.common.utils.bean.CharSetter; import org.apache.servicecomb.foundation.common.utils.bean.DoubleGetter; import org.apache.servicecomb.foundation.common.utils.bean.DoubleSetter; import org.apache.servicecomb.foundation.common.utils.bean.FloatGetter; import org.apache.servicecomb.foundation.common.utils.bean.FloatSetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.common.utils.bean.ShortGetter; import org.apache.servicecomb.foundation.common.utils.bean.ShortSetter; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; public final class LambdaMetafactoryUtils { private static final Lookup LOOKUP = MethodHandles.lookup(); private static final Map, Class> GETTER_MAP = new HashMap<>(); private static final Map, Class> SETTER_MAP = new HashMap<>(); static { initGetterSetterMap(); } private static void initGetterSetterMap() { GETTER_MAP.put(boolean.class, BoolGetter.class); GETTER_MAP.put(byte.class, ByteGetter.class); GETTER_MAP.put(char.class, CharGetter.class); GETTER_MAP.put(short.class, ShortGetter.class); GETTER_MAP.put(int.class, IntGetter.class); GETTER_MAP.put(long.class, LongGetter.class); GETTER_MAP.put(float.class, FloatGetter.class); GETTER_MAP.put(double.class, DoubleGetter.class); SETTER_MAP.put(boolean.class, BoolSetter.class); SETTER_MAP.put(byte.class, ByteSetter.class); SETTER_MAP.put(char.class, CharSetter.class); SETTER_MAP.put(short.class, ShortSetter.class); SETTER_MAP.put(int.class, IntSetter.class); SETTER_MAP.put(long.class, LongSetter.class); SETTER_MAP.put(float.class, FloatSetter.class); SETTER_MAP.put(double.class, DoubleSetter.class); } private LambdaMetafactoryUtils() { } private static Method findAbstractMethod(Class functionalInterface) { for (Method method : functionalInterface.getMethods()) { if ((method.getModifiers() & Modifier.ABSTRACT) != 0) { return method; } } return null; } @SuppressWarnings("unchecked") public static T createLambda(Object instance, Method instanceMethod, Class functionalIntfCls) { try { Method intfMethod = findAbstractMethod(functionalIntfCls); MethodHandle methodHandle = LOOKUP.unreflect(instanceMethod); MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes()); MethodType instanceMethodType = MethodType .methodType(instanceMethod.getReturnType(), instanceMethod.getParameterTypes()); CallSite callSite = LambdaMetafactory.metafactory( LOOKUP, intfMethod.getName(), MethodType.methodType(functionalIntfCls, instance.getClass()), intfMethodType, methodHandle, instanceMethodType); return (T) callSite.getTarget().bindTo(instance).invoke(); } catch (Throwable e) { throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e); } } @SuppressWarnings("unchecked") public static T createLambda(Method instanceMethod, Class functionalIntfCls) { if (Modifier.isNative(instanceMethod.getModifiers())) { // fix "Failed to create lambda from public final native java.lang.Class java.lang.Object.getClass()" return null; } try { Method intfMethod = findAbstractMethod(functionalIntfCls); MethodHandle methodHandle = LOOKUP.unreflect(instanceMethod); MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes()); // the return type of fluent setter is object instead of void, but we can assume the return type is void. it doesn't matter MethodType instanceMethodType = MethodType .methodType(intfMethod.getReturnType(), methodHandle.type().parameterList()); CallSite callSite = LambdaMetafactory.metafactory( LOOKUP, intfMethod.getName(), MethodType.methodType(functionalIntfCls), intfMethodType, methodHandle, instanceMethodType); return (T) callSite.getTarget().invoke(); } catch (Throwable e) { throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e); } } public static T createGetter(Method getMethod) { Class getterCls = GETTER_MAP.getOrDefault(getMethod.getReturnType(), Getter.class); return createLambda(getMethod, getterCls); } @SuppressWarnings("unchecked") public static Getter createObjectGetter(Method getMethod) { return createLambda(getMethod, Getter.class); } public static Getter createObjectGetter(BeanPropertyDefinition propertyDefinition) { if (propertyDefinition.hasGetter()) { return createObjectGetter(propertyDefinition.getGetter().getAnnotated()); } return createGetter(propertyDefinition.getField().getAnnotated()); } // slower than reflect directly @SuppressWarnings("unchecked") public static Getter createGetter(Field field) { checkAccess(field); return instance -> { try { return (F) field.get(instance); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }; } private static void checkAccess(Field field) { // This check is not accurate. Most of time package visible and protected access can be ignored, so simply do this. if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) { throw new IllegalStateException( String.format("Can not access field, a public field or accessor is required." + "Declaring class is %s, field is %s", field.getDeclaringClass().getName(), field.getName())); } } public static T createSetter(Method setMethod) { Class setterCls = SETTER_MAP.getOrDefault(setMethod.getParameterTypes()[0], Setter.class); return createLambda(setMethod, setterCls); } // just for avoid java 9~11 bug: https://bugs.openjdk.java.net/browse/JDK-8174983 // otherwise can be replaced by: createLambda(setMethod, Setter.class) @SuppressWarnings("unchecked") public static Setter createObjectSetter(Method setMethod) { Object setter = createSetter(setMethod); if (setter instanceof BoolSetter) { return (Instance, value) -> ((BoolSetter) setter).set(Instance, (boolean) value); } if (setter instanceof ByteSetter) { return (Instance, value) -> ((ByteSetter) setter).set(Instance, (byte) value); } if (setter instanceof CharSetter) { return (Instance, value) -> ((CharSetter) setter).set(Instance, (char) value); } if (setter instanceof DoubleSetter) { return (Instance, value) -> ((DoubleSetter) setter).set(Instance, (double) value); } if (setter instanceof FloatSetter) { return (Instance, value) -> ((FloatSetter) setter).set(Instance, (float) value); } if (setter instanceof IntSetter) { return (Instance, value) -> ((IntSetter) setter).set(Instance, (int) value); } if (setter instanceof LongSetter) { return (Instance, value) -> ((LongSetter) setter).set(Instance, (long) value); } if (setter instanceof ShortSetter) { return (Instance, value) -> ((ShortSetter) setter).set(Instance, (short) value); } return (Setter) setter; } public static Setter createObjectSetter(BeanPropertyDefinition propertyDefinition) { if (propertyDefinition.hasSetter()) { return createObjectSetter(propertyDefinition.getSetter().getAnnotated()); } return createSetter(propertyDefinition.getField().getAnnotated()); } // slower than reflect directly public static Setter createSetter(Field field) { checkAccess(field); return (instance, value) -> { try { field.set(instance, value); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.function.Consumer; public class LambdaUtils { public static Consumer ignoreException(Consumer consumer) { return item -> { try { consumer.accept(item); } catch (Throwable e) { } }; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java ================================================ /* * Copyright 2014 Red Hat, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. * */ /* * Forked from https://github.com/vert-x3/vertx-web/blob/927ed057ddc028eb09a168db621de3d72fd85ed4/vertx-web/src/main/java/io/vertx/ext/web/impl/Utils.java * Because we uses getSortedAcceptableMimeTypes method which is removed by vertx. */ package org.apache.servicecomb.foundation.common.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.regex.Pattern; public class MimeTypesUtils { private static final Pattern COMMA_SPLITTER = Pattern.compile(" *, *"); private static final Pattern SEMICOLON_SPLITTER = Pattern.compile(" *; *"); private static final Pattern EQUAL_SPLITTER = Pattern.compile(" *= *"); private static final Comparator ACCEPT_X_COMPARATOR = (o1, o2) -> Float.compare(getQuality(o2), getQuality(o1)); private static float getQuality(String s) { if (s == null) { return 0; } String[] params = SEMICOLON_SPLITTER.split(s); for (int i = 1; i < params.length; i++) { String[] q = EQUAL_SPLITTER.split(params[1]); if ("q".equals(q[0])) { return Float.parseFloat(q[1]); } } return 1; } public static List getSortedAcceptableMimeTypes(String acceptHeader) { // accept anything when accept is not present if (acceptHeader == null) { return Collections.emptyList(); } // parse String[] items = COMMA_SPLITTER.split(acceptHeader); // sort on quality Arrays.sort(items, ACCEPT_X_COMPARATOR); List list = new ArrayList<>(items.length); for (String item : items) { // find any ; e.g.: "application/json;q=0.8" int space = item.indexOf(';'); if (space != -1) { list.add(item.substring(0, space)); } else { list.add(item); } } return list; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MuteExceptionUtil { public interface FunctionWithException { R apply(T t) throws Exception; } public interface FunctionWithDoubleParam { R apply(T1 t1, T2 t2) throws Exception; } private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); public static class MuteExceptionUtilBuilder { private String logMessage; private Object[] customMessageParams; public MuteExceptionUtilBuilder withLog(String message, Object... params) { this.logMessage = message; this.customMessageParams = params; return this; } private String getLogMessage(String defaultMessage) { return logMessage != null ? logMessage : defaultMessage; } public R executeFunction(FunctionWithException function, T t) { try { return function.apply(t); } catch (Exception e) { LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); return null; } } public T executeSupplier(Supplier supplier) { try { return supplier.get(); } catch (Exception e) { LOGGER.error(getLogMessage("execute Supplier failure..."), customMessageParams, e); return null; } } public T executeCompletableFuture(CompletableFuture completableFuture) { try { return completableFuture.get(); } catch (Exception e) { LOGGER.error(getLogMessage("execute CompletableFuture failure..."), customMessageParams, e); return null; } } public R executeFunctionWithDoubleParam(FunctionWithDoubleParam function, T1 t1, T2 t2) { try { return function.apply(t1, t2); } catch (Exception e) { LOGGER.error(getLogMessage("execute FunctionWithDoubleParam failure..."), customMessageParams, e); return null; } } } public static MuteExceptionUtilBuilder builder() { return new MuteExceptionUtilBuilder(); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/PartUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.File; import java.io.InputStream; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.common.part.ResourcePart; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import jakarta.servlet.http.Part; public final class PartUtils { private PartUtils() { } public static Part getSinglePart(String name, Object value) { if (value == null) { return null; } if (value instanceof Part) { return (Part) value; } if (value instanceof InputStream) { return new InputStreamPart(name, (InputStream) value); } if (value instanceof Resource) { return new ResourcePart(name, (Resource) value); } if (value instanceof File) { return new FilePart(name, (File) value); } if (value instanceof byte[]) { return new ResourcePart(name, new ByteArrayResource((byte[]) value)); } throw new IllegalStateException( String.format("File input parameter of %s could be %s / %s / %s / byte[] or %s, but got %s.", name, Part.class.getName(), InputStream.class.getName(), Resource.class.getName(), File.class.getName(), value.getClass().getName())); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ReflectUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.apache.commons.lang3.reflect.FieldUtils; import org.springframework.util.ReflectionUtils; import com.google.common.reflect.TypeToken; public final class ReflectUtils { private ReflectUtils() { } public static void setField(Object instance, String fieldName, Object value) { setField(instance.getClass(), instance, fieldName, value); } public static void setField(Class cls, Object instance, String fieldName, Object value) { Field field = ReflectionUtils.findField(cls, fieldName); try { field.setAccessible(true); field.set(instance, value); } catch (Exception e) { throw new Error(e); } } // 根据方法名,忽略参数查找method,调用此函数的前提是没有重载 public static Method findMethod(Class cls, String methodName) { for (Method method : cls.getMethods()) { if (method.getName().equals(methodName)) { return method; } } return null; } @SuppressWarnings("unchecked") public static Class getFieldArgument(Class genericCls, String fieldName) { try { Type generic = FieldUtils.getField(genericCls, fieldName).getGenericType(); TypeToken token = TypeToken.of(genericCls).resolveType(generic); Type fieldType = token.getType(); Type argument = ((ParameterizedType) fieldType).getActualTypeArguments()[0]; if (argument instanceof GenericArrayType) { return (Class) TypeToken.of(argument).getRawType(); } return (Class) argument; } catch (Throwable e) { throw new IllegalStateException("Failed to get generic argument.", e); } } @SuppressWarnings("unchecked") public static T constructArrayType(Class cls) { return (T) Array.newInstance(cls, 0).getClass(); } public static Class getClassByName(String clsName) { try { return Class.forName(clsName); } catch (ClassNotFoundException e) { ClassLoader classLoader = JvmUtils.correctClassLoader(null); try { return classLoader.loadClass(clsName); } catch (ClassNotFoundException e1) { return null; } } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ResourceUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.function.Predicate; import java.util.stream.Stream; public final class ResourceUtil { private ResourceUtil() { } /** * Search the specified location in classpath, and returns the resources with the specified suffix. */ public static List findResourcesBySuffix(String resourceLocation, String fileNameSuffix) throws IOException, URISyntaxException { return findResources(resourceLocation, path -> path.toString().endsWith(fileNameSuffix)); } /** * Search the specified location in classpath, all the resources found are collected and returned. */ public static List findResources(String resourceLocation) throws IOException, URISyntaxException { return findResources(resourceLocation, p -> true); } /** * Search the specified location in classpath, which can be a directory or the exact file location, * and returns a list of URIs pointing to the matched resources. * * @param resourceLocation in which location the resources are searched * @param filter to pick out those matched resources */ public static List findResources(String resourceLocation, Predicate filter) throws IOException, URISyntaxException { ArrayList result = new ArrayList<>(); Enumeration dirURLs = JvmUtils.findClassLoader().getResources(resourceLocation); while (dirURLs.hasMoreElements()) { URL dirURL = dirURLs.nextElement(); if ("file".equals(dirURL.getProtocol())) { Path dirPath = Paths.get(dirURL.toURI()); collectResourcesFromPath(dirPath, filter, result); continue; } try (FileSystem fileSystem = FileSystems.newFileSystem(dirURL.toURI(), Collections.emptyMap())) { Path dirPath = fileSystem.getPath(resourceLocation); if (Files.exists(dirPath)) { // normal jar files like : xxx.jar!/resourceLocation collectResourcesFromPath(dirPath, filter, result); } else { // spring boot fat jar files like : xxx.jar!/BOOT-INF/!classes/resourceLocation dirPath = fileSystem.getPath("BOOT-INF", "classes", resourceLocation); if (Files.exists(dirPath)) { collectResourcesFromPath(dirPath, filter, result); } } } } return result; } private static void collectResourcesFromPath(Path path, Predicate filter, Collection resources) throws IOException { try (Stream dirContentTraversalStream = Files.walk(path)) { dirContentTraversalStream .filter(filter) .map(Path::toUri) .forEach(resources::add); } } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/RestObjectMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser.Feature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.vertx.core.json.JsonObject; public class RestObjectMapper extends AbstractRestObjectMapper { private static class JsonObjectSerializer extends JsonSerializer { @Override public void serialize(JsonObject value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeObject(value.getMap()); } } private static final long serialVersionUID = -8158869347066287575L; private static final JavaType STRING_JAVA_TYPE = TypeFactory.defaultInstance().constructType(String.class); public RestObjectMapper() { super(); getFactory().disable(Feature.AUTO_CLOSE_SOURCE); // Enable features that can tolerance errors and not enable those make more constraints for compatible reasons. // Developers can use validation api to do more checks. disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // no view annotations shouldn't be included in JSON this._deserializationConfig = this._deserializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); this._serializationConfig = this._serializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); SimpleModule module = new SimpleModule(); // custom types module.addSerializer(JsonObject.class, new JsonObjectSerializer()); registerModule(module); registerModule(new JavaTimeModule()); } public RestObjectMapper(RestObjectMapper src) { super(src); } @Override public RestObjectMapper copy() { this._checkInvalidCopy(RestObjectMapper.class); return new RestObjectMapper(this); } @Override public String convertToString(Object value) throws Exception { return convertValue(value, STRING_JAVA_TYPE); } @Override @SuppressWarnings("unchecked") public T convertValue(Object fromValue, JavaType toValueType) throws IllegalArgumentException { // After jackson 2.10.*, will by pass the following check when convert value. But this is useful // for java chassis applications and do not need to convert to keep performance. So add the check here.(conversion is // not necessary and will cause some trouble in some user applications that depend on this) if (fromValue == null) { return null; } else { Class targetType = toValueType.getRawClass(); if (targetType != Object.class && !toValueType.hasGenericTypes() && targetType.isAssignableFrom(fromValue.getClass())) { return (T) fromValue; } } return super.convertValue(fromValue, toValueType); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/StringBuilderUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public final class StringBuilderUtils { private StringBuilderUtils() { } public static void appendLine(StringBuilder sb, String fmt, Object... args) { sb.append(String.format(fmt, args)).append("\n"); } public static StringBuilder deleteLast(StringBuilder sb, int count) { int min = Math.min(sb.length(), count); sb.setLength(sb.length() - min); return sb; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TimeUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.time.Clock; public final class TimeUtils { private TimeUtils() { } private static final Clock systemDefaultZoneClock = Clock.systemDefaultZone(); /** * @return Singleton system default zone clock */ public static Clock getSystemDefaultZoneClock() { return systemDefaultZoneClock; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TypesUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; /** * common utils to convert java types. */ public class TypesUtil { private static final Map, Class> PRIMITIVE_TO_WRAPPER = new HashMap<>(); static { PRIMITIVE_TO_WRAPPER.put(byte.class, Byte.class); PRIMITIVE_TO_WRAPPER.put(short.class, Short.class); PRIMITIVE_TO_WRAPPER.put(int.class, Integer.class); PRIMITIVE_TO_WRAPPER.put(long.class, Long.class); PRIMITIVE_TO_WRAPPER.put(float.class, Float.class); PRIMITIVE_TO_WRAPPER.put(double.class, Double.class); PRIMITIVE_TO_WRAPPER.put(boolean.class, Boolean.class); PRIMITIVE_TO_WRAPPER.put(char.class, Character.class); } private static final Map, Class> WRAPPER_TO_PRIMITIVE = new HashMap<>(); static { WRAPPER_TO_PRIMITIVE.put(Byte.class, byte.class); WRAPPER_TO_PRIMITIVE.put(Short.class, short.class); WRAPPER_TO_PRIMITIVE.put(Integer.class, int.class); WRAPPER_TO_PRIMITIVE.put(Long.class, long.class); WRAPPER_TO_PRIMITIVE.put(Float.class, float.class); WRAPPER_TO_PRIMITIVE.put(Double.class, double.class); WRAPPER_TO_PRIMITIVE.put(Boolean.class, boolean.class); WRAPPER_TO_PRIMITIVE.put(Character.class, char.class); } public static final JavaType PRIMITIVE_BYTE = TypeFactory.defaultInstance().constructType(byte.class); public static final JavaType PRIMITIVE_SHORT = TypeFactory.defaultInstance().constructType(short.class); public static final JavaType PRIMITIVE_INT = TypeFactory.defaultInstance().constructType(int.class); public static final JavaType PRIMITIVE_LONG = TypeFactory.defaultInstance().constructType(long.class); public static final JavaType PRIMITIVE_FLOAT = TypeFactory.defaultInstance().constructType(float.class); public static final JavaType PRIMITIVE_DOUBLE = TypeFactory.defaultInstance().constructType(double.class); public static final JavaType PRIMITIVE_BOOLEAN = TypeFactory.defaultInstance().constructType(boolean.class); public static final JavaType PRIMITIVE_CHAR = TypeFactory.defaultInstance().constructType(char.class); public static final JavaType PRIMITIVE_WRAPPER_BYTE = TypeFactory.defaultInstance().constructType(Byte.class); public static final JavaType PRIMITIVE_WRAPPER_SHORT = TypeFactory.defaultInstance().constructType(Short.class); public static final JavaType PRIMITIVE_WRAPPER_INT = TypeFactory.defaultInstance().constructType(Integer.class); public static final JavaType PRIMITIVE_WRAPPER_LONG = TypeFactory.defaultInstance().constructType(Long.class); public static final JavaType PRIMITIVE_WRAPPER_FLOAT = TypeFactory.defaultInstance().constructType(Float.class); public static final JavaType PRIMITIVE_WRAPPER_DOUBLE = TypeFactory.defaultInstance().constructType(Double.class); public static final JavaType PRIMITIVE_WRAPPER_BOOLEAN = TypeFactory.defaultInstance().constructType(Boolean.class); public static final JavaType PRIMITIVE_WRAPPER_CHAR = TypeFactory.defaultInstance().constructType(Character.class); private static final Map PRIMITIVE_TO_WRAPPER_JAVATYPE = new HashMap<>(); static { PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_BYTE, PRIMITIVE_WRAPPER_BYTE); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_SHORT, PRIMITIVE_WRAPPER_SHORT); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_INT, PRIMITIVE_WRAPPER_INT); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_LONG, PRIMITIVE_WRAPPER_LONG); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_FLOAT, PRIMITIVE_WRAPPER_FLOAT); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_DOUBLE, PRIMITIVE_WRAPPER_DOUBLE); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_BOOLEAN, PRIMITIVE_WRAPPER_BOOLEAN); PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_CHAR, PRIMITIVE_WRAPPER_CHAR); } private static final Map WRAPPER_TO_PRIMITIVE_JAVATYPE = new HashMap<>(); static { WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_BYTE, PRIMITIVE_BYTE); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_SHORT, PRIMITIVE_SHORT); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_INT, PRIMITIVE_INT); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_LONG, PRIMITIVE_LONG); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_FLOAT, PRIMITIVE_FLOAT); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_DOUBLE, PRIMITIVE_DOUBLE); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_BOOLEAN, PRIMITIVE_BOOLEAN); WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_CHAR, PRIMITIVE_CHAR); } public static Class primitiveTypeToWrapper(Class primitiveType) { return PRIMITIVE_TO_WRAPPER.get(primitiveType); } public static Class wrapperTypeToPrimitive(Class wrapperType) { return WRAPPER_TO_PRIMITIVE.get(wrapperType); } public static JavaType primitiveJavaTypeToWrapper(JavaType primitiveType) { return PRIMITIVE_TO_WRAPPER_JAVATYPE.get(primitiveType); } public static JavaType wrapperJavaTypeToPrimitive(JavaType wrapperType) { return WRAPPER_TO_PRIMITIVE_JAVATYPE.get(wrapperType); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArrayGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public class ArrayGetter implements Getter { private final int idx; public ArrayGetter(int idx) { this.idx = idx; } @Override public T get(T[] instance) { return instance[idx]; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArraySetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public class ArraySetter implements Setter { private final int idx; public ArraySetter(int idx) { this.idx = idx; } @Override public void set(T[] instance, T value) { instance[idx] = value; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface BoolGetter { boolean get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface BoolSetter { void set(T instance, boolean value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface ByteGetter { byte get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface ByteSetter { void set(T instance, byte value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface CharGetter { char get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface CharSetter { void set(T instance, char value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface DoubleGetter { double get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface DoubleSetter { void set(T instance, double value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface FloatGetter { float get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface FloatSetter { void set(T instance, float value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Getter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface Getter { F get(C instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface IntGetter { int get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface IntSetter { void set(T instance, int value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface LongGetter { long get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface LongSetter { void set(T instance, long value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; import java.util.Map; public class MapGetter implements Getter, V> { private final K key; public MapGetter(K key) { this.key = key; } @Override public V get(Map instance) { return instance.get(key); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; import java.util.Map; public class MapSetter implements Setter, V> { private final K key; public MapSetter(K key) { this.key = key; } @Override public void set(Map instance, V value) { instance.put(key, value); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Setter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface Setter { void set(C instance, F value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortGetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface ShortGetter { short get(T instance); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.bean; public interface ShortSetter { void set(T instance, short value); } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartDeserializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.json; import java.io.IOException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import jakarta.servlet.http.Part; public class JavaxServletPartDeserializer extends JsonDeserializer { @Override public Part deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { final Object currentValue = p.getEmbeddedObject(); return (Part) currentValue; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartSerializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.json; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.databind.util.TokenBuffer; import jakarta.servlet.http.Part; // 什么情况下用? public class JavaxServletPartSerializer extends StdSerializer { private static final long serialVersionUID = 348443113789878443L; public JavaxServletPartSerializer() { this(null); } protected JavaxServletPartSerializer(Class t) { super(t); } @Override public void serialize(Part value, JsonGenerator gen, SerializerProvider provider) throws IOException { ObjectCodec preservedCodec = ((TokenBuffer) gen).asParser().getCodec(); // set codec as null to avoid recursive dead loop // JsonGenerator is instantiated for each serialization, so there should be no thread safe issue gen.setCodec(null); gen.writeObject(value); gen.setCodec(preservedCodec); } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/PartModule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils.json; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import com.fasterxml.jackson.databind.module.SimpleModule; import jakarta.servlet.http.Part; public class PartModule extends SimpleModule implements SPIOrder { private static final long serialVersionUID = 4201325332650814739L; public PartModule() { super("javax-servlet-part"); addSerializer(Part.class, new JavaxServletPartSerializer()); addDeserializer(Part.class, new JavaxServletPartDeserializer()); } @Override public Object getTypeId() { return getModuleName(); } @Override public int getOrder() { return Short.MAX_VALUE; } } ================================================ FILE: foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/token/Keypair4Auth.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.token; import java.security.PrivateKey; import java.security.PublicKey; /** * 进程级别公私钥对 * */ public class Keypair4Auth { private Keypair4Auth() { } private PrivateKey privateKey; private PublicKey publicKey; private String publicKeyEncoded; public PrivateKey getPrivateKey() { return privateKey; } public void setPrivateKey(PrivateKey privateKey) { this.privateKey = privateKey; } public PublicKey getPublicKey() { return publicKey; } public void setPublicKey(PublicKey publicKey) { this.publicKey = publicKey; } public String getPublicKeyEncoded() { return publicKeyEncoded; } public void setPublicKeyEncoded(String publicKeyEncoded) { this.publicKeyEncoded = publicKeyEncoded; } public static Keypair4Auth INSTANCE = new Keypair4Auth(); } ================================================ FILE: foundations/foundation-common/src/main/resources/META-INF/mime.types ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. application/octet-stream bin application/astound asd,asn application/fastman lcc application/java-archive jar application/java-serialized-object ser application/java-vm class application/mac-binhex40 hqx application/x-stuffit sit application/mbedlet mbd application/msword doc,dot,wiz,rtf application/oda oda application/pdf pdf application/postscript ai,eps,ps application/studiom smp application/timbuktu tbt application/vnd.ms-excel xls,xlw,xla,xlc,xlm,xlt application/vnd.ms-powerpoint ppt,pps,pot application/vnd.ms-project mpp application/winhlp hlp application/x-javascript js application/x-javascript;charset=UTF-8 jsu application/x-java-jnlp-file jnlp application/x-aim aim application/x-asap asp application/x-csh csh application/x-dvi dvi application/x-earthtime etc application/x-envoy evy application/x-gtar gtar application/x-cpio cpio application/x-hdf hdf application/x-latex latex application/x-javascript-config jsc application/x-maker fm application/x-mif mif,mi application/x-mocha mocha,moc application/x-msaccess mdb application/x-mscardfile crd application/x-msclip clp application/x-msmediaview m13,m14 application/x-msmetafile wmf application/x-msmoney mny application/x-mspublisher pub application/x-msschedule scd application/x-msterminal trm application/x-mswrite wri application/x-NET-Install ins application/x-netcdf nc,cdf application/x-ns-proxy-autoconfig proxy application/x-salsa slc application/x-sh sh application/x-shar shar application/x-sprite spr,sprite application/x-tar tar application/x-tcl tcl application/x-perl pl application/x-tex tex application/x-texinfo texinfo,texi application/x-timbuktu tbp application/x-tkined tki,tkined application/x-troff-man man application/x-troff-me me application/x-troff-ms ms application/x-troff t,tr,roff application/x-wais-source src application/zip zip application/pre-encrypted enc application/x-pkcs7-crl crl application/x-fortezza-ckl ckl application/xml-dtd dtd audio/basic au,snd audio/echospeech es,esl audio/midi midi,mid audio/x-aiff aif,aiff,aifc audio/x-wav wav audio/x-pn-realaudio ra,ram audio/x-pac pac audio/x-epac pae audio/x-liveaudio lam drawing/x-dwf dwf image/fif fif image/x-icon ico image/gif gif image/ief ief image/ifs ifs image/jpeg jpeg,jpg,jpe,jfif,pjpeg,pjp image/png png image/tiff tiff,tif image/vnd dwg,svf image/wavelet wi image/bmp bmp image/x-photo-cd pcd image/x-cmu-raster ras image/x-portable-anymap pnm image/x-portable-bitmap pbm image/x-portable-graymap pgm image/x-portable-pixmap ppm image/x-rgb rgb image/x-xbitmap xbm image/x-xpixmap xpm image/x-xwindowdump xwd text/css css text/html htm,html text/plain txt text/richtext rtx text/tab-separated-values tsv text/x-setext etx text/x-speech talk text/xml xml text/xul xul video/isivideo fvi video/mpeg mpeg,mpg,mpe,mpv,vbs,mpegv video/x-mpeg2 mpv2,mp2v video/msvideo avi video/quicktime qt,mov,moov video/vivo viv,vivo video/wavelet wv video/x-sgi-movie movie x-world/x-svr svr x-world/x-vrml wrl x-world/x-vrt vrt x-conference/x-cooltalk ice magnus-internal/imagemap map magnus-internal/parsed-html shtml magnus-internal/cgi cgi,exe,bat application/x-x509-ca-cert cacert application/x-x509-server-cert scert application/x-x509-user-cert ucert application/x-x509-email-cert ecert ================================================ FILE: foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.foundation.common.encrypt.NoEncryption ================================================ FILE: foundations/foundation-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.foundation.auth.FoundationCommonAuthConfiguration org.apache.servicecomb.foundation.common.event.EventManager ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/DynamicObjectTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common; import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; import io.vertx.core.json.jackson.DatabindCodec; class DynamicObjectTest { @Test void should_support_json_encode_decode() { Map map = new HashMap<>(); map.put("k", "v"); DynamicObject dynamicObject = DatabindCodec.mapper().convertValue(map, DynamicObject.class); assertThat(dynamicObject.getDynamic()).isNotSameAs(map); assertThat(dynamicObject.getDynamic()).isEqualTo(map); assertThat(Json.encode(dynamicObject)).isEqualTo("{\"k\":\"v\"}"); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/base/DynamicEnumTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.base; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.annotation.JsonCreator; import io.vertx.core.json.Json; import io.vertx.core.json.jackson.DatabindCodec; class DynamicEnumTest { static final String UNKNOWN_COLOR = "\"UNKNOWN-COLOR\""; static class Color extends DynamicEnum { public static final Color RED = new Color("RED"); public static final Color BLUE = new Color("BLUE"); private static final DynamicEnumCache CACHE = new DynamicEnumCache<>(Color.class); public Color(String value) { super(value); } @JsonCreator public static Color fromValue(String value) { return CACHE.fromValue(value); } } static class ColorModel { public Color color; } @Test void should_be_dynamic_for_unknown_value() { Color color = Color.fromValue("unknown"); assertThat(color.isDynamic()).isTrue(); assertThat(color.isStatic()).isFalse(); } @Test void should_be_static_for_known_value() { Color color = Color.fromValue("RED"); assertThat(color.isDynamic()).isFalse(); assertThat(color.isStatic()).isTrue(); } @Test void should_encode() { assertThat(Json.encode(Color.RED)).isEqualTo("\"RED\""); } @Test void should_be_null_when_convert_from_null() { assertThat(DatabindCodec.mapper().convertValue(null, Color.class)).isNull(); } @Test void should_be_null_when_decode_from_null() { ColorModel model = Json.decodeValue(Json.encode(new ColorModel()), ColorModel.class); assertThat(model.color).isNull(); } @Test void should_decode_from_known_value() { Color color = Json.decodeValue(Json.encode(Color.RED), Color.class); assertThat(color).isEqualTo(Color.RED); } @Test void should_decode_from_unknown_value() { Color color = Json.decodeValue(UNKNOWN_COLOR, Color.class); assertThat(color).isEqualTo(Color.fromValue("UNKNOWN-COLOR")); } @Test void should_not_cache_unknown_value() { Color value1 = Json.decodeValue(UNKNOWN_COLOR, Color.class); Color value2 = Json.decodeValue(UNKNOWN_COLOR, Color.class); assertThat(value1).isNotSameAs(value2); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/cache/TestVersionedCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.cache; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import mockit.Deencapsulation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestVersionedCache { private static final AtomicInteger VERSION = Deencapsulation.getField(VersionedCache.class, "VERSION"); @Test public void construct() { VersionedCache cache = new VersionedCache(); Assertions.assertNull(cache.data()); Assertions.assertNull(cache.arrayData()); Assertions.assertTrue(cache.isEmpty()); } @Test public void autoCacheVersion() { VersionedCache cache = new VersionedCache().autoCacheVersion(); Assertions.assertEquals(VERSION.get(), cache.cacheVersion()); } @Test public void setCacheVersion() { VersionedCache parent = new VersionedCache().autoCacheVersion(); VersionedCache cache = new VersionedCache().cacheVersion(parent.cacheVersion()); Assertions.assertEquals(parent.cacheVersion(), cache.cacheVersion()); } @Test public void setName() { VersionedCache cache = new VersionedCache().name("n"); Assertions.assertEquals("n", cache.name()); } @Test public void setSubName() { VersionedCache parent = new VersionedCache().name("parent"); VersionedCache child = new VersionedCache().subName(parent, "child"); Assertions.assertEquals("parent/child", child.name()); } @Test public void setMapData() { VersionedCache cache = new VersionedCache().data(Collections.emptyMap()); Assertions.assertSame(Collections.emptyMap(), cache.data()); Assertions.assertSame(Collections.emptyMap(), cache.mapData()); Assertions.assertTrue(cache.isEmpty()); cache.data(Collections.singletonMap("k", "v")); Assertions.assertFalse(cache.isEmpty()); } @Test public void setCollectionData() { VersionedCache cache = new VersionedCache().data(Collections.emptyList()); Assertions.assertSame(Collections.emptyList(), cache.data()); Assertions.assertSame(Collections.emptyList(), cache.collectionData()); Assertions.assertTrue(cache.isEmpty()); cache.data(Collections.singletonList("v")); Assertions.assertFalse(cache.isEmpty()); } @Test public void setArrayData() { Object[] array = Collections.emptyList().toArray(); VersionedCache cache = new VersionedCache().data(array); Assertions.assertSame(array, cache.data()); Assertions.assertSame(array, cache.arrayData()); Assertions.assertTrue(cache.isEmpty()); cache.data(new String[] {"a", "b"}); Assertions.assertFalse(cache.isEmpty()); } @Test public void setCommonData() { VersionedCache cache = new VersionedCache().data(null); Assertions.assertNull(cache.data()); Assertions.assertNull(cache.arrayData()); Assertions.assertTrue(cache.isEmpty()); cache.data("a"); Assertions.assertFalse(cache.isEmpty()); } @Test public void isExpired() { VersionedCache cacheOld = new VersionedCache().autoCacheVersion(); VersionedCache cacheNew = new VersionedCache().autoCacheVersion(); Assertions.assertTrue(cacheOld.isExpired(cacheNew)); Assertions.assertFalse(cacheOld.isExpired(cacheOld)); Assertions.assertFalse(cacheNew.isExpired(cacheOld)); } @Test public void isSameVersion() { VersionedCache cacheOld = new VersionedCache().autoCacheVersion(); VersionedCache cacheNew = new VersionedCache().autoCacheVersion(); VersionedCache cacheSame = new VersionedCache().cacheVersion(cacheNew.cacheVersion()); Assertions.assertFalse(cacheOld.isSameVersion(cacheNew)); Assertions.assertTrue(cacheSame.isSameVersion(cacheNew)); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/concurrency/RunnableWrapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.concurrency; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class RunnableWrapperTest { /** * {@link SuppressedRunnableWrapper} should ensure that any {@link Throwable} thrown from {@link Runnable} * should not interrupt the scheduled tasks. */ @Test public void run() { ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); CountDownLatch countDownLatch = new CountDownLatch(3); try { scheduledThreadPoolExecutor.scheduleAtFixedRate( new SuppressedRunnableWrapper( () -> { countDownLatch.countDown(); switch ((int) countDownLatch.getCount()) { case 2: throw new Error("Normal case, this is a mocked error"); case 1: throw new IllegalStateException("Normal case, this is a mocked exception"); default: } } ), 1, 1, TimeUnit.MILLISECONDS); countDownLatch.await(1, TimeUnit.SECONDS); } catch (InterruptedException e) { System.out.println("get an InterruptedException! " + e.getMessage()); e.printStackTrace(); } finally { scheduledThreadPoolExecutor.shutdownNow(); } Assertions.assertEquals(0, countDownLatch.getCount()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/config/BeanProp.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.config; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class BeanProp { @JsonProperty(value = "1.2") private String test; public String getTest() { return test; } public void setTest(String test) { this.test = test; } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/encrypt/TestEncryptions.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.encrypt; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestEncryptions { static class MyEncryption implements Encryption { @Override public char[] decode(char[] encrypted, String tags) { if (tags == null) { return null; } return encrypted; } @Override public char[] encode(char[] plain, String tags) { if (tags == null) { return null; } return plain; } } @Test public void testEncryptions() { Assertions.assertNull(Encryptions.decode((String) null, "")); Assertions.assertEquals(Encryptions.decode("abcd", ""), "abcd"); Assertions.assertEquals(Encryptions.decode("abcd", null), "abcd"); Assertions.assertNull(Encryptions.encode((String) null, "")); Assertions.assertEquals(Encryptions.encode("abcd", ""), "abcd"); Assertions.assertEquals(Encryptions.decode("abcd", null), "abcd"); } @Test public void testEncryptionsMy() { Encryption old = Encryptions.getEncryption(); Encryptions.setEncryption(new MyEncryption()); Assertions.assertNull(Encryptions.decode((String) null, "")); Assertions.assertEquals(Encryptions.decode("abcd", ""), "abcd"); Assertions.assertNull(Encryptions.decode("abcd", null)); Assertions.assertNull(Encryptions.encode((String) null, "")); Assertions.assertEquals(Encryptions.encode("abcd", ""), "abcd"); Assertions.assertNull(Encryptions.encode("abcd", null)); Encryptions.setEncryption(old); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventBus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import java.util.ArrayList; import java.util.List; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import org.junit.jupiter.api.Test; public class TestEventBus { private final EventBus eventBus = new SimpleEventBus(); private final List events = new ArrayList<>(); public static class SubscriberForTest { private final List events; public SubscriberForTest(List events) { this.events = events; } @Subscribe @AllowConcurrentEvents public void s1(Integer event) { events.add(event); } @Subscribe public void s2(String event) { events.add(event); } } public static class SubscriberWithOrderForTest { private final List events; public SubscriberWithOrderForTest(List events) { this.events = events; } @Subscribe @SubscriberOrder(100) public void s1(String event) { events.add("s1:" + event); } @Subscribe @SubscriberOrder(-100) public void s2(String event) { events.add("s2:" + event); } } @Test public void order() { eventBus.register(new SubscriberWithOrderForTest(events)); eventBus.post("value"); MatcherAssert.assertThat(events, Matchers.contains("s2:value", "s1:value")); } @Test public void unregister() { Object obj = new SubscriberForTest(events); eventBus.register(obj); eventBus.unregister(obj); } @Test public void oneSubscriber() { Object obj = new SubscriberForTest(events); eventBus.register(obj); eventBus.post(0.1); eventBus.post(1); eventBus.post("str"); MatcherAssert.assertThat(events, Matchers.contains(1, "str")); eventBus.unregister(obj); events.clear(); eventBus.post(0.1); eventBus.post(1); eventBus.post("str"); MatcherAssert.assertThat(events, Matchers.empty()); } @Test public void twoSubscriber() { Object obj1 = new SubscriberForTest(events); Object obj2 = new SubscriberForTest(events); eventBus.register(obj1); eventBus.register(obj2); eventBus.post(0.1); eventBus.post(1); eventBus.post("str"); MatcherAssert.assertThat(events, Matchers.contains(1, 1, "str", "str")); events.clear(); eventBus.unregister(obj1); eventBus.post(0.1); eventBus.post(1); eventBus.post("str"); MatcherAssert.assertThat(events, Matchers.contains(1, "str")); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.event; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import com.google.common.eventbus.Subscribe; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestEventManager { private int objCount = 0; private int iCount = 0; public void test(Object listener) { EventManager.register(listener); EventManager.post("a"); EventManager.post(1); Assertions.assertEquals(2, objCount); Assertions.assertEquals(1, iCount); EventManager.unregister(listener); EventManager.post("a"); EventManager.post(1); Assertions.assertEquals(2, objCount); Assertions.assertEquals(1, iCount); } @Test public void normalListener() { LogCollector collector = new LogCollector(); test(this); Assertions.assertTrue(collector.getEvents().isEmpty()); // ensure no warning logs collector.tearDown(); } @Test public void anonymousListener() { LogCollector collector = new LogCollector(); Object listener = new Object() { @Subscribe private void onObject(Object obj) { objCount++; } @Subscribe void onInt(Integer obj) { iCount++; } }; try { test(listener); Assertions.fail(); } catch (IllegalStateException e) { Assertions.assertTrue(true); } collector.tearDown(); } @Test public void anonymousListenerPublic() { LogCollector collector = new LogCollector(); Object listener = new Object() { @Subscribe public void onObject(Object obj) { objCount++; } @Subscribe public void onInt(Integer obj) { iCount++; } }; try { test(listener); Assertions.fail(); } catch (IllegalStateException e) { Assertions.assertTrue(true); } // ensure logs: "LOGGER.warn("Failed to create lambda for method: {}, fallback to reflect.", method, throwable);" Assertions.assertFalse(collector.getEvents().isEmpty()); collector.tearDown(); } @Subscribe public void onObject(Object obj) { objCount++; } @Subscribe public void onInt(Integer obj) { iCount++; } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status.Family; public class TestHttpStatus { @Test public void testIsSuccessCode() { Assertions.assertTrue(HttpStatus.isSuccess(200)); Assertions.assertFalse(HttpStatus.isSuccess(300)); } @Test public void testIsSuccessType() { Assertions.assertTrue(HttpStatus.isSuccess(Status.OK)); Assertions.assertFalse(HttpStatus.isSuccess(Status.BAD_REQUEST)); } @Test public void testGetStatusCode() { HttpStatus status = new HttpStatus(200, "ok"); Assertions.assertEquals(200, status.getStatusCode()); } @Test public void testGetFamily() { HttpStatus status = new HttpStatus(200, "ok"); Assertions.assertEquals(Family.familyOf(200), status.getFamily()); } @Test public void testGetReasonPhrase() { HttpStatus status = new HttpStatus(200, "ok"); Assertions.assertEquals("ok", status.getReasonPhrase()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatusUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import jakarta.ws.rs.core.Response.StatusType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttpStatusUtils { HttpStatusManager mgr = new HttpStatusManager(); StatusType st; @Test public void testStandard() { st = HttpStatusUtils.getOrCreateByStatusCode(200); Assertions.assertEquals(200, st.getStatusCode()); } @Test public void testNotStandard() { st = mgr.getOrCreateByStatusCode(250); Assertions.assertEquals(250, st.getStatusCode()); } @Test public void testAddRepeat() { IllegalStateException illegalStateException = Assertions.assertThrows(IllegalStateException.class, () -> mgr.addStatusType(new HttpStatus(200, "ok"))); Assertions.assertEquals("repeated status code: 200", illegalStateException.getMessage()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.http; import java.net.URISyntaxException; import jakarta.ws.rs.core.MediaType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttpUtils { @Test public void parseParamFromHeaderValue_normal() { Assertions.assertEquals("a", HttpUtils.parseParamFromHeaderValue("key1=a;key2=b", "key1")); Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=a;key2= b", "key2")); Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=a; key2=b", "key2")); Assertions.assertEquals("a", HttpUtils.parseParamFromHeaderValue("key1=\"a\";key2=\"b\"", "key1")); Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=\"a\";key2=\" b\"", "key2")); Assertions.assertEquals("b", HttpUtils.parseParamFromHeaderValue("key1=\"a\"; key2=\"b\"", "key2")); Assertions.assertEquals("b c.txt", HttpUtils.parseParamFromHeaderValue("key1=\"a\"; key2=\"b c.txt\"", "key2")); } @Test public void parseParamFromHeaderValue_null() { Assertions.assertNull(HttpUtils.parseParamFromHeaderValue(null, "key")); Assertions.assertNull(HttpUtils.parseParamFromHeaderValue("key1=a; key2=b", "key")); } @Test public void parseParamFromHeaderValue_emptyStr() { Assertions.assertEquals("", HttpUtils.parseParamFromHeaderValue("key1=a; key2=", "key2")); } @Test public void uriEncode_null() { Assertions.assertEquals("", HttpUtils.uriEncodePath(null)); } @Test public void uriDecode_null() { Assertions.assertNull(HttpUtils.uriDecodePath(null)); } @Test public void uriEncode_chineseAndSpace() { String encoded = HttpUtils.uriEncodePath("测 试"); Assertions.assertEquals("%E6%B5%8B%20%E8%AF%95", encoded); Assertions.assertEquals("测 试", HttpUtils.uriDecodePath(encoded)); } @Test public void uriEncode_failed() { IllegalArgumentException illegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, () -> HttpUtils.uriEncodePath(":")); Assertions.assertEquals("uriEncode failed, path=\":\".", illegalArgumentException.getMessage()); Assertions.assertTrue(illegalArgumentException.getCause() instanceof URISyntaxException); } @Test public void uriEncode_plus() { String encoded = HttpUtils.uriEncodePath("a+b"); Assertions.assertEquals("a+b", encoded); Assertions.assertEquals("a+b", HttpUtils.uriDecodePath(encoded)); } @Test public void uriEncode_encodeEntirePath() { String encoded = HttpUtils.uriEncodePath("a%%'+b/def"); Assertions.assertEquals("a%25%25'+b/def", encoded); } @Test public void pathParamEncode() { Assertions.assertEquals("a+b", HttpUtils.encodePathParam("a+b")); Assertions.assertEquals("a%25b", HttpUtils.encodePathParam("a%b")); Assertions.assertEquals("a%25%25b", HttpUtils.encodePathParam("a%%b")); Assertions.assertEquals("%3C%20%3E'%22%EF%BC%88)&%2F%20%20", HttpUtils.encodePathParam("< >'\"()&/ ")); Assertions.assertEquals("%E6%B5%8B%20%E8%AF%95", HttpUtils.encodePathParam("测 试")); } /** * SafeChar: the characters that are not encoded. * This test is to show those safe chars excepting 0..9, a..z and A..Z */ @Test public void pathParamEncode_SafeChar() { Assertions.assertEquals("-._~!$'()*,;&=@:+", HttpUtils.encodePathParam("-._~!$'()*,;&=@:+")); } @Test public void uriDecode_failed() { IllegalArgumentException illegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, () -> HttpUtils.uriDecodePath(":")); Assertions.assertEquals("uriDecode failed, path=\":\".", illegalArgumentException.getMessage()); Assertions.assertTrue(illegalArgumentException.getCause() instanceof URISyntaxException); } @Test public void parseFileNameFromHeaderValue() { String fileName = "测 试.txt"; String encoded = HttpUtils.uriEncodePath(fileName); Assertions.assertEquals(fileName, HttpUtils.parseFileNameFromHeaderValue("xx;filename=" + encoded)); } @Test public void parseFileNameFromHeaderValue_defaultName() { Assertions.assertEquals("default", HttpUtils.parseFileNameFromHeaderValue("xx")); } @Test public void parseFileNameFromHeaderValue_ignorePath() { Assertions.assertEquals("a.txt", HttpUtils.parseFileNameFromHeaderValue("xx;filename=../../a.txt")); } @Test public void getCharsetFromContentType_noContentType() { String character = HttpUtils.getCharsetFromContentType(null); Assertions.assertNull(character); } @Test public void getCharsetFromContentType_noCharset() { String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON); Assertions.assertNull(character); } @Test public void getCharsetFromContentType_noSemicolonEnd() { String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=utf-8"); Assertions.assertEquals("utf-8", character); } @Test public void getCharsetFromContentType_semicolonEnd() { String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=utf-8;"); Assertions.assertEquals("utf-8", character); } @Test public void getCharsetFromContentType_needTrim() { String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset= utf-8 ;"); Assertions.assertEquals("utf-8", character); } @Test public void getCharsetFromContentType_quotationMarks() { String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=\"utf-8\";"); Assertions.assertEquals("utf-8", character); } @Test public void getCharsetFromContentType_quotationMarks_needTrim() { String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=\" utf-8 \";"); Assertions.assertEquals("utf-8", character); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestIpPort.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.net; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestIpPort { @Test public void testIpPort() { IpPort inst1 = new IpPort(); inst1.setHostOrIp("localhost"); inst1.setPort(3333); IpPort inst2 = new IpPort("localhost", 3333); Assertions.assertEquals(inst1.getHostOrIp(), inst2.getHostOrIp()); Assertions.assertEquals(inst1.getPort(), inst2.getPort()); Assertions.assertEquals(inst1.getSocketAddress().getHostName(), inst2.getSocketAddress().getHostName()); Assertions.assertEquals(inst1, inst1); Assertions.assertEquals(inst1, inst2); Assertions.assertEquals(inst1.toString(), "localhost:3333"); Assertions.assertNotEquals(inst1, new Object()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.net; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import mockit.Deencapsulation; public class TestNetUtils { @Test public void testFindProperHostAddress() { NetUtils.resetHostName(); String result = NetUtils.getHostName(); System.out.println(result); Assertions.assertNotNull(result); result = NetUtils.getHostAddress(); System.out.println(result); Assertions.assertNotNull(result); result = NetUtils.getIpv6HostAddress(); System.out.println(result); if (result != null) { Assertions.assertFalse(result.contains("%")); } } @Test public void testIpPort() { IpPort oIPPort = new IpPort("10.145.154.45", 8080); Assertions.assertEquals("10.145.154.45", oIPPort.getHostOrIp()); Assertions.assertEquals(8080, oIPPort.getPort()); oIPPort.setPort(9090); Assertions.assertEquals(9090, oIPPort.getPort()); Assertions.assertNotEquals(null, oIPPort.getSocketAddress()); } @Test public void testNetUtils() { Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1:8080").getHostOrIp()); Assertions.assertEquals(8080, NetUtils.parseIpPort("127.0.0.1:8080").getPort()); Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1").getHostOrIp()); Assertions.assertEquals(-1, NetUtils.parseIpPort("127.0.0.1").getPort()); Assertions.assertNull(NetUtils.parseIpPort((String) null)); Assertions.assertNull(NetUtils.parseIpPortFromURI(null)); Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPortFromURI("rest://127.0.0.1:8080").getHostOrIp()); Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("http://127.0.0.1:8080").getPort()); Assertions.assertEquals(80, NetUtils.parseIpPortFromURI("http://127.0.0.1").getPort()); Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("https://127.0.0.1:8080").getPort()); Assertions.assertEquals(443, NetUtils.parseIpPortFromURI("https://127.0.0.1").getPort()); Assertions.assertEquals(30000, NetUtils.parseIpPort("http", "127.0.0.1:30000").getPort()); Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("http", "127.0.0.1:30000").getHostOrIp()); Assertions.assertEquals(30000, NetUtils.parseIpPort("https", "127.0.0.1:30000").getPort()); Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("https", "127.0.0.1:30000").getHostOrIp()); Assertions.assertEquals(80, NetUtils.parseIpPort("http", "127.0.0.1").getPort()); Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("http", "127.0.0.1").getHostOrIp()); Assertions.assertEquals(443, NetUtils.parseIpPort("https", "127.0.0.1").getPort()); Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("https", "127.0.0.1").getHostOrIp()); Assertions.assertNull(NetUtils.parseIpPort("http", null)); checkException(v -> NetUtils.parseIpPort("127.0.0.18080")); checkException(v -> NetUtils.parseIpPortFromURI("ss")); } @Test public void testNetUtilsIPv6() { Assertions.assertEquals("[::1]", NetUtils.parseIpPort("[::1]:8080").getHostOrIp()); Assertions.assertEquals("[::]", NetUtils.parseIpPort("[::]:8080").getHostOrIp()); Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", NetUtils.parseIpPort("[fe80::f816:3eff:feda:38cd%eth0]:8080").getHostOrIp()); Assertions.assertEquals("[fe80::38f7:44b8:8ab1:468%16]", NetUtils.parseIpPort("[fe80::38f7:44b8:8ab1:468%16]:8080").getHostOrIp()); Assertions.assertEquals(8080, NetUtils.parseIpPort("[::1]:8080").getPort()); Assertions.assertEquals(8080, NetUtils.parseIpPort("[::]:8080").getPort()); Assertions.assertEquals(8080, NetUtils.parseIpPort("[fe80::f816:3eff:feda:38cd%eth0]:8080").getPort()); Assertions.assertEquals(8080, NetUtils.parseIpPort("[fe80::38f7:44b8:8ab1:468%16]:8080").getPort()); Assertions.assertEquals("[::1]", NetUtils.parseIpPortFromURI("rest://[::1]:8080").getHostOrIp()); Assertions.assertEquals("[::]", NetUtils.parseIpPortFromURI("rest://[::]:8080").getHostOrIp()); Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", NetUtils.parseIpPortFromURI("rest://[fe80::f816:3eff:feda:38cd%eth0]:8080").getHostOrIp()); Assertions.assertEquals("[fe80::38f7:44b8:8ab1:468%16]", NetUtils.parseIpPortFromURI("rest://[fe80::38f7:44b8:8ab1:468%16]:8080").getHostOrIp()); Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("rest://[::1]:8080").getPort()); Assertions.assertEquals(80, NetUtils.parseIpPortFromURI("http://[::1]").getPort()); Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("https://[::1]:8080").getPort()); Assertions.assertEquals(443, NetUtils.parseIpPortFromURI("https://[::1]").getPort()); Assertions.assertEquals(30000, NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getPort()); Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getHostOrIp()); Assertions.assertEquals(30000, NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getPort()); Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getHostOrIp()); Assertions.assertEquals(80, NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]").getPort()); Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]").getHostOrIp()); Assertions.assertEquals(443, NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]").getPort()); Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]").getHostOrIp()); } @Test public void testFullOperation() { Assertions.assertNotNull(NetUtils.getHostAddress()); Assertions.assertNotNull(NetUtils.getHostName()); } @Test public void testGetRealListenAddress() { Assertions.assertNull(NetUtils.getRealListenAddress("http", null)); Assertions.assertEquals("http://1.1.1.1:8080", NetUtils.getRealListenAddress("http", "1.1.1.1:8080")); checkException(v -> NetUtils.getRealListenAddress("http:1", "1.1.1.1:8080")); } @Test public void testNetworkInterface() { Map org = Deencapsulation.getField(NetUtils.class, "allInterfaceAddresses"); Map newValue = new HashMap<>(); InetAddress addr = Mockito.mock(InetAddress.class); newValue.put("eth100", addr); Deencapsulation.setField(NetUtils.class, "allInterfaceAddresses", newValue); Assertions.assertEquals(addr, NetUtils.getInterfaceAddress("eth100")); Assertions.assertEquals(addr, NetUtils.ensureGetInterfaceAddress("eth100")); try { NetUtils.ensureGetInterfaceAddress("xxx"); Assertions.fail("must throw exception"); } catch (IllegalArgumentException e) { Assertions.assertEquals("Can not find address for interface name: xxx", e.getMessage()); } Deencapsulation.setField(NetUtils.class, "allInterfaceAddresses", org); } @Test public void testCanTcpListenNo() throws IOException { InetAddress address = InetAddress.getByName("127.0.0.1"); try (ServerSocket ss = new ServerSocket(0, 0, address)) { Assertions.assertFalse(NetUtils.canTcpListen(address, ss.getLocalPort())); } } @Test public void testCanTcpListenYes() throws IOException { InetAddress address = InetAddress.getByName("127.0.0.1"); ServerSocket ss = new ServerSocket(0, 0, address); int port = ss.getLocalPort(); ss.close(); Assertions.assertTrue(NetUtils.canTcpListen(address, port)); } @Test public void humanReadableBytes() { Assertions.assertEquals("0", NetUtils.humanReadableBytes(0L)); Assertions.assertEquals("1", NetUtils.humanReadableBytes(1L)); Assertions.assertEquals("1023", NetUtils.humanReadableBytes(1023L)); Assertions.assertEquals("1.000K", NetUtils.humanReadableBytes(1024L)); Assertions.assertEquals("1.001K", NetUtils.humanReadableBytes(1025L)); Assertions.assertEquals("1023.999K", NetUtils.humanReadableBytes(1024L * 1024 - 1)); Assertions.assertEquals("1.000M", NetUtils.humanReadableBytes(1024L * 1024)); Assertions.assertEquals("1.000M", NetUtils.humanReadableBytes(1024L * 1024 + 1)); Assertions.assertEquals("1.001M", NetUtils.humanReadableBytes(1024L * 1024 + 1024)); Assertions.assertEquals("1023.999M", NetUtils.humanReadableBytes(1024L * 1024 * 1024 - 1024)); Assertions.assertEquals("1024.000M", NetUtils.humanReadableBytes(1024L * 1024 * 1024 - 1)); Assertions.assertEquals("1.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024)); Assertions.assertEquals("1.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 + 1)); Assertions.assertEquals("1.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 + 1024)); Assertions.assertEquals("1023.999G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 - 1024 * 1024)); Assertions.assertEquals("1024.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 - 1024)); Assertions.assertEquals("1.000T", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024)); Assertions.assertEquals("1.001T", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 + 1024 * 1024 * 1024)); Assertions.assertEquals("1023.999T", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 - 1024L * 1024 * 1024)); Assertions.assertEquals("1.000P", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024)); Assertions.assertEquals("1.001P", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 + 1024L * 1024 * 1024 * 1024)); Assertions.assertEquals("1023.999P", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 * 1024 - 1024L * 1024 * 1024 * 1024)); Assertions.assertEquals("1.000E", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 * 1024)); Assertions.assertEquals("1.001E", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 * 1024 + 1024L * 1024 * 1024 * 1024 * 1024)); Assertions.assertEquals("8.000E", NetUtils.humanReadableBytes(Long.MAX_VALUE)); } @Test public void testGetHostName() { Assertions.assertNotEquals(null, NetUtils.getHostName()); Deencapsulation.setField(NetUtils.class, "hostName", null); Assertions.assertNotEquals(null, NetUtils.getHostName()); } @Test public void testGetHostAddress() { Assertions.assertNotEquals(null, NetUtils.getHostAddress()); Deencapsulation.setField(NetUtils.class, "hostAddress", null); Assertions.assertNotEquals(null, NetUtils.getHostAddress()); } public void checkException(Consumer testedBehavior) { try { testedBehavior.accept(null); Assertions.fail("IllegalArgumentException is expected!"); } catch (Exception e) { Assertions.assertEquals(IllegalArgumentException.class, e.getClass()); } } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.net; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import mockit.Deencapsulation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestURIEndpointObject { @Test public void testRestEndpointObject() { URIEndpointObject obj = new URIEndpointObject("http://127.0.2.0:8080"); Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); Assertions.assertEquals(8080, obj.getPort()); Assertions.assertFalse(obj.isSslEnabled()); obj = new URIEndpointObject("http://127.0.2.0:8080?sslEnabled=true"); Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); Assertions.assertEquals(8080, obj.getPort()); Assertions.assertTrue(obj.isSslEnabled()); Assertions.assertNull(obj.getFirst("notExist")); obj = new URIEndpointObject("http://127.0.2.0:8080?sslEnabled=true&protocol=http2"); Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); Assertions.assertEquals(8080, obj.getPort()); Assertions.assertTrue(obj.isSslEnabled()); Assertions.assertTrue(obj.isHttp2Enabled()); Assertions.assertNull(obj.getFirst("notExist")); obj = new URIEndpointObject("rest://127.0.2.0:8080?urlPrefix=%2Froot"); Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); Assertions.assertEquals(8080, obj.getPort()); Assertions.assertEquals("/root", obj.getQuery("urlPrefix").get(0)); } @Test public void testRestEndpointObjectException() { Assertions.assertThrows(IllegalArgumentException.class, () -> new URIEndpointObject("http://127.0.2.0")); } @Test public void testQueryChineseAndSpaceAndEmpty() throws UnsupportedEncodingException { String strUri = "cse://1.1.1.1:1234/abc?a=1&b=&country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()); URIEndpointObject ep = new URIEndpointObject(strUri); Map> queries = Deencapsulation.getField(ep, "queries"); Assertions.assertEquals(3, queries.size()); Assertions.assertEquals(1, ep.getQuery("a").size()); Assertions.assertEquals("1", ep.getFirst("a")); Assertions.assertEquals(1, ep.getQuery("b").size()); Assertions.assertEquals("", ep.getFirst("b")); Assertions.assertEquals(1, ep.getQuery("country").size()); Assertions.assertEquals("中 国", ep.getFirst("country")); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestAbstractPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.IOException; import jakarta.ws.rs.core.MediaType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractPart { AbstractPart part = new AbstractPart(); private void checkError(Error error) { Assertions.assertEquals("not supported method", error.getMessage()); } @Test public void getInputStream() throws IOException { Error error = Assertions.assertThrows(Error.class, () -> part.getInputStream()); checkError(error); } @Test public void getContentType() throws IOException { Assertions.assertEquals(MediaType.APPLICATION_OCTET_STREAM, part.getContentType()); String contentType = "abc"; part.contentType(contentType); Assertions.assertEquals(contentType, part.getContentType()); } @Test public void getName() throws IOException { Assertions.assertNull(part.getName()); String name = "abc"; part.name = name; Assertions.assertEquals(name, part.getName()); } @Test public void getSubmittedFileName() throws IOException { Assertions.assertNull(part.getSubmittedFileName()); String submittedFileName = "abc"; part.setSubmittedFileName(submittedFileName); Assertions.assertEquals(submittedFileName, part.getSubmittedFileName()); } @Test public void setSubmittedFileName_contentTypeNotNull() { part.contentType(MediaType.TEXT_PLAIN); part.setSubmittedFileName("a.zip"); Assertions.assertEquals(MediaType.TEXT_PLAIN, part.getContentType()); } @Test public void setSubmittedFileName_setNull() { part.setSubmittedFileName(null); Assertions.assertEquals(MediaType.APPLICATION_OCTET_STREAM, part.getContentType()); } @Test public void setSubmittedFileName_setNormal() { part.setSubmittedFileName("a.zip"); Assertions.assertEquals("application/zip", part.getContentType()); } @Test public void getSize() { Error error = Assertions.assertThrows(Error.class, () -> part.getSize()); checkError(error); } @Test public void write() throws IOException { Error error = Assertions.assertThrows(Error.class, () -> part.write("file")); checkError(error); } @Test public void delete() throws IOException { Error error = Assertions.assertThrows(Error.class, () -> part.delete()); checkError(error); } @Test public void getHeader() { Error error = Assertions.assertThrows(Error.class, () -> part.getHeader("header")); checkError(error); } @Test public void getHeaders() { Error error = Assertions.assertThrows(Error.class, () -> part.getHeaders("header")); checkError(error); } @Test public void getHeaderNames() { Error error = Assertions.assertThrows(Error.class, () -> part.getHeaderNames()); checkError(error); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestFilePart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class TestFilePart { static File file = new File("testFilePart.txt"); static String content = "testFilePart content"; String name = "paramName"; FilePart part = new FilePart(name, file.getAbsolutePath()); @BeforeAll public static void setup() throws IOException { file.delete(); FileUtils.write(file, content, StandardCharsets.UTF_8); } @AfterAll public static void teardown() { file.delete(); } @Test public void getName() { Assertions.assertEquals(name, part.getName()); } @Test public void getInputStream() throws IOException { try (InputStream is = part.getInputStream()) { Assertions.assertEquals(content, IOUtils.toString(is, StandardCharsets.UTF_8)); } } @Test public void getSize() { Assertions.assertEquals(content.length(), part.getSize()); } @Test public void write() throws IOException { File destFile = new File("testFilePartCopy.txt"); part.write(destFile.getPath()); Assertions.assertEquals(content, FileUtils.readFileToString(destFile, StandardCharsets.UTF_8)); FilePart destPart = new FilePart(null, destFile); destPart.delete(); Assertions.assertFalse(destFile.exists()); } @Test public void deleteAfterFinished() { Assertions.assertFalse(part.isDeleteAfterFinished()); Assertions.assertTrue(part.setDeleteAfterFinished(true).isDeleteAfterFinished()); } @Test public void getAbsolutePath() { Assertions.assertEquals(file.getAbsolutePath(), part.getAbsolutePath()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestInputStreamPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestInputStreamPart { String name = "paramName"; byte[] bytes = new byte[] {1, 2, 3}; InputStream is = new ByteArrayInputStream(bytes); InputStreamPart part = new InputStreamPart(name, is); @Test public void getName() { Assertions.assertEquals(name, part.getName()); } @Test public void test() throws IOException { try (InputStream is = part.getInputStream()) { byte[] content = IOUtils.toByteArray(is); Assertions.assertArrayEquals(bytes, content); } } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestResourcePart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.part; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; public class TestResourcePart { String name = "paramName"; byte[] bytes = new byte[] {1, 2, 3}; Resource resource = new ByteArrayResource(bytes); ResourcePart part = new ResourcePart(name, resource); @Test public void getName() { Assertions.assertEquals(name, part.getName()); } @Test public void getInputStream() throws IOException { try (InputStream is = part.getInputStream()) { byte[] content = IOUtils.toByteArray(is); Assertions.assertArrayEquals(bytes, content); } } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.spring; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestPaasNamespaceHandler { PaasNamespaceHandler paasNamespaceHandler = null; @BeforeEach public void setUp() throws Exception { paasNamespaceHandler = new PaasNamespaceHandler(); } @AfterEach public void tearDown() throws Exception { paasNamespaceHandler = null; } @Test public void testInit() { boolean status = false; try { paasNamespaceHandler.init(); } catch (Exception e) { status = true; } Assertions.assertFalse(status); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/AsyncUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.junit.jupiter.api.Test; class AsyncUtilsTest { @Test void should_convert_success_async_to_sync() { CompletableFuture future = CompletableFuture.completedFuture("value"); assertThat(AsyncUtils.toSync(future)).isEqualTo("value"); } @Test void should_convert_fail_async_to_sync() { RuntimeExceptionWithoutStackTrace throwable = new RuntimeExceptionWithoutStackTrace(); CompletableFuture future = AsyncUtils.completeExceptionally(throwable); Throwable catchThrowable = catchThrowable(() -> AsyncUtils.toSync(future)); assertThat(catchThrowable).isSameAs(throwable); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/JsonUtilsTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; public class JsonUtilsTest { @Test void testWriteUnicodeValueAsString() throws JsonProcessingException { String unicodeStr = "测试"; Map inMap = new HashMap<>(); inMap.put("key", unicodeStr); Assertions.assertFalse(StringUtils.isAsciiPrintable(JsonUtils.writeValueAsString(inMap))); String jsonStr = JsonUtils.writeUnicodeValueAsString(inMap); Assertions.assertTrue(StringUtils.isAsciiPrintable(jsonStr)); Map outMap = JsonUtils.OBJ_MAPPER.readValue(jsonStr, new TypeReference>() { }); Assertions.assertEquals(unicodeStr, outMap.get("key")); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/ResourceUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class ResourceUtilTest { @Test public void loadResources_in_disk() throws IOException, URISyntaxException { List uris = ResourceUtil.findResourcesBySuffix("META-INF/spring", ".xml"); Assertions.assertEquals(1, uris.size()); URI uri = uris.get(0); Assertions.assertTrue(uri.toString().startsWith("file:"), "unexpected uri: " + uri); Assertions.assertTrue(uri.toString().endsWith("META-INF/spring/config.bean.xml"), "unexpected uri: " + uri); } @Test public void loadResources_exact_file_in_disk() throws IOException, URISyntaxException { List uris = ResourceUtil.findResources("META-INF/spring/config.bean.xml"); Assertions.assertEquals(1, uris.size()); URI uri = uris.get(0); Assertions.assertTrue(uri.toString().startsWith("file:"), "unexpected uri: " + uri); Assertions.assertTrue(uri.toString().endsWith("META-INF/spring/config.bean.xml"), "unexpected uri: " + uri); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestExceptionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestExceptionUtils { @Test public void testExceptionUtils() { Exception e = new Exception("Hello"); Assertions.assertEquals("cause:Exception,message:Hello", ExceptionUtils.getExceptionMessageWithoutTrace(e)); Exception e2 = new Exception("FAIL", new IOException("IO")); Assertions.assertEquals("cause:Exception,message:FAIL;cause:IOException,message:IO", ExceptionUtils.getExceptionMessageWithoutTrace(e2)); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.File; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestFileNameTooLong { private static final Logger LOGGER = LoggerFactory.getLogger(TestFileNameTooLong.class); // ensure the maximum file path do not exceed. now set to 200(some old windows max 260) // smaller is better, can refactor in future // NOTICE: compiled classes or other generated files like surefire reports may exceed this size private static final int MAN_FILE_SIZE = 200; @Test public void assertFileNotTooLong() { File folder = new File(System.getProperty("user.dir")); LOGGER.error(folder.getAbsolutePath()); // $ROOT\foundations\foundation-common File root = new File(folder.getParentFile().getParent()); LOGGER.error(root.getAbsolutePath()); // $ROOT\foundations\foundation-common Assertions.assertTrue(root.exists()); Assertions.assertTrue(root.isDirectory()); List names = new LinkedList<>(); findLongFileName(root, names, root.getAbsolutePath().length()); Collections.sort(names); names.forEach(LOGGER::error); if (!names.isEmpty()) { // for debug Assertions.assertEquals("", names.toString()); } Assertions.assertTrue(names.isEmpty()); } private static void findLongFileName(File folder, List holder, int baseLength) { if (folder.isFile()) { if (folder.getAbsolutePath().length() >= MAN_FILE_SIZE + baseLength) { holder.add(folder.getAbsolutePath()); } } else if (folder.isDirectory() && !"target".equals(folder.getName())) { File[] children = folder.listFiles(); for (File child : children) { findLongFileName(child, holder, baseLength); } } else { return; } } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFortifyUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFortifyUtils { @Test public void testFortifyUtils() throws IOException { Assertions.assertEquals("", FortifyUtils.getErrorMsg(null)); Assertions.assertEquals("", FortifyUtils.getErrorStack(null)); } @Test public void testFilePerm() { Assertions.assertEquals(10, (FilePerm.getDefaultAclPerm().size())); Assertions.assertEquals(3, (FilePerm.getDefaultPosixPerm().size())); Assertions.assertEquals(4, (FilePerm.getPosixPerm(400).size())); } @Test public void testGetErrorMsg() { Throwable e = new Throwable(); FortifyUtils.getErrorMsg(e); Assertions.assertNull(FortifyUtils.getErrorMsg(e)); } @Test public void testGetSecurityXmlDocumentFactory() { try { FortifyUtils.getSecurityXmlDocumentFactory(); Assertions.assertNotNull(FortifyUtils.getSecurityXmlDocumentFactory()); } catch (Exception e) { /* Do not Worry */ Assertions.fail(); } } @Test public void testGetErrorStack() { Throwable e = new Throwable(); FortifyUtils.getErrorStack(e); Assertions.assertNotEquals(true, FortifyUtils.getErrorStack(e)); } @Test public void testGetErrorInfo() { Throwable e = new Throwable(); FortifyUtils.getErrorInfo(e, true); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestGenericsUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.core.ParameterizedTypeReference; public class TestGenericsUtils { @Test public void testIsGenerics() { Assertions.assertTrue(GenericsUtils.isGenericResponseType(List.class)); Assertions.assertTrue(GenericsUtils.isGenericResponseType(Map.class)); Assertions.assertTrue(GenericsUtils.isGenericResponseType(Person.class)); Assertions.assertFalse(GenericsUtils.isGenericResponseType(Man.class)); Assertions.assertFalse(GenericsUtils.isGenericResponseType(String.class)); Assertions.assertFalse(GenericsUtils.isGenericResponseType(GenericsUtils.class)); Assertions.assertFalse(GenericsUtils.isGenericResponseType(new ParameterizedTypeReference>() { }.getType())); } } class Person { private T value; public T getValue() { return value; } } class Man extends Person { } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestIOUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestIOUtils { @Test public void testAnonymousPath() { Assertions.assertEquals(":/:/r/2/y/b/y/0/s/microservice.yaml", IOUtils.anonymousPath("jar:file:/D:/User/.m2/repository/servicecomb" + "/transport-highway/2.3.0/classes/microservice.yaml")); Assertions.assertEquals(":/:/r/microservice.yaml", IOUtils.anonymousPath("file:/D:/User/microservice.yaml")); Assertions.assertEquals(":\\:\\r\\microservice.yaml", IOUtils.anonymousPath("file:\\D:\\User\\microservice.yaml")); Assertions.assertEquals("r\\t\\a.txt", IOUtils.anonymousPath("user\\test\\a.txt")); Assertions.assertEquals(":\\:\\a.txt", IOUtils.anonymousPath("file:\\D:\\a.txt")); Assertions.assertEquals(":\\a.txt", IOUtils.anonymousPath("D:\\a.txt")); Assertions.assertEquals("a.txt", IOUtils.anonymousPath("a.txt")); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import mockit.Mock; import mockit.MockUp; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URL; public class TestJvmUtils { @Test @Disabled public void findMainClass_notExist() { System.clearProperty(JvmUtils.SUN_JAVA_COMMAND); Assertions.assertNull(JvmUtils.findMainClass()); } @Test @Disabled public void findMainClass_existButEmpty() { System.setProperty(JvmUtils.SUN_JAVA_COMMAND, ""); Assertions.assertNull(JvmUtils.findMainClass()); } @Test @Disabled public void findMainClass_invalid() { LogCollector logCollector = new LogCollector(); System.setProperty(JvmUtils.SUN_JAVA_COMMAND, "invalidCls"); Assertions.assertNull(JvmUtils.findMainClass()); Assertions.assertEquals("\"invalidCls\" is not a valid class.", logCollector.getEvents().get(0).getMessage()); logCollector.tearDown(); } @Test @Disabled public void findMainClass_class_normal() { System.setProperty(JvmUtils.SUN_JAVA_COMMAND, TestJvmUtils.class.getName() + " arg"); Assertions.assertEquals(TestJvmUtils.class, JvmUtils.findMainClass()); } @Test @Disabled public void findMainClass_jar_normal() throws Exception { String command = "a.jar"; String content = String.format("Manifest-Version: 1.0\nMain-Class: %s\n", TestJvmUtils.class.getName()); InputStream inputStream = new ByteArrayInputStream(content.getBytes()); new MockUp() { @Mock InputStream openStream() throws Exception { return inputStream; } }; System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg"); Assertions.assertEquals(TestJvmUtils.class, JvmUtils.findMainClass()); } @Test @Disabled public void findMainClass_jar_null() throws Exception { String content = "Manifest-Version: 1.0\n"; InputStream inputStream = new ByteArrayInputStream(content.getBytes()); String command = "a.jar"; new MockUp() { @Mock InputStream openStream() throws Exception { return inputStream; } }; System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg"); Assertions.assertNull(JvmUtils.findMainClass()); } @Test @Disabled @EnabledOnJre(JRE.JAVA_17) public void findMainClass_jar_readFailed() throws Exception { String command = "a.jar"; new MockUp() { @Mock InputStream openStream() throws Exception { throw new RuntimeExceptionWithoutStackTrace(); } }; System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg"); Assertions.assertNull(JvmUtils.findMainClass()); } @Test @Disabled public void findMainClassByStackTrace_normal() throws Exception{ StackTraceElement[] stackTraceElements = { new StackTraceElement("declaring.class.fileName", "methodName", "fileName", 100), new StackTraceElement("java.lang.String", "main", "fileName", 120) }; new MockUp() { @Mock public StackTraceElement[] getStackTrace() { return stackTraceElements; } }; Assertions.assertEquals(String.class, JvmUtils.findMainClassByStackTrace()); } @Test @Disabled public void findMainClassByStackTrace_invalidClass() throws Exception{ StackTraceElement[] stackTraceElements = { new StackTraceElement("declaring.class.fileName", "methodName", "fileName", 100), new StackTraceElement("InvalidClass", "main", "fileName", 120) }; new MockUp() { @Mock public StackTraceElement[] getStackTrace() { return stackTraceElements; } }; Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); } @Test @Disabled public void findMainClassByStackTrace_withoutMainMethod() throws Exception{ StackTraceElement[] stackTraceElements = { new StackTraceElement("declaring.class.fileName", "methodName", "fileName", 100), new StackTraceElement("InvalidClass", "methodName", "fileName", 120) }; new MockUp() { @Mock public StackTraceElement[] getStackTrace() { return stackTraceElements; } }; Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); } @Test @Disabled public void findMainClassByStackTrace_emptyStackTrace() throws Exception{ new MockUp() { @Mock public StackTraceElement[] getStackTrace() { return new StackTraceElement[]{}; } }; Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); } @Test @Disabled public void findMainClassByStackTrace_nullStackTrace() throws Exception{ new MockUp() { @Mock public StackTraceElement[] getStackTrace() { return null; } }; Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaMetafactoryUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import static org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils.createObjectGetter; import static org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils.createObjectSetter; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; @EnabledOnJre(JRE.JAVA_8) public class TestLambdaMetafactoryUtils { public static class Model { public int f1; private int f2; public int getF1() { return f1; } public void setF1(int f1) { this.f1 = f1; } public Model fluentSetF1(int f1) { this.f1 = f1; return this; } public List echo(List value) { return value; } } Model model = new Model(); @SuppressWarnings("unchecked") @Test public void createLambda_withInstance() throws Throwable { Supplier getter = LambdaMetafactoryUtils .createLambda(model, Model.class.getMethod("getF1"), Supplier.class); Consumer setter = LambdaMetafactoryUtils .createLambda(model, Model.class.getMethod("setF1", int.class), Consumer.class); Function echo = LambdaMetafactoryUtils .createLambda(model, Model.class.getMethod("echo", List.class), Function.class); setter.accept(1); int f1 = (int) getter.get(); Assertions.assertEquals(1, f1); MatcherAssert.assertThat((List) echo.apply(Arrays.asList(2)), Matchers.contains(2)); } @SuppressWarnings("unchecked") @Test public void createGetterSetterByMethod() throws Throwable { IntGetter getter = LambdaMetafactoryUtils.createGetter(Model.class.getMethod("getF1")); IntSetter setter = LambdaMetafactoryUtils.createSetter(Model.class.getMethod("setF1", int.class)); IntSetter fluentSetter = LambdaMetafactoryUtils .createSetter(Model.class.getMethod("fluentSetF1", int.class)); BiFunction echo = LambdaMetafactoryUtils .createLambda(Model.class.getMethod("echo", List.class), BiFunction.class); setter.set(model, 1); int f1 = getter.get(model); Assertions.assertEquals(1, f1); MatcherAssert.assertThat((List) echo.apply(model, Arrays.asList(2)), Matchers.contains(2)); fluentSetter.set(model, 2); int ff1 = getter.get(model); Assertions.assertEquals(2, ff1); } @Test public void should_failed_when_createGetterSetterByField_and_field_is_not_public() throws Throwable { Field field = Model.class.getDeclaredField("f2"); assertThat(catchThrowable(() -> LambdaMetafactoryUtils.createGetter(field))) .isInstanceOf(IllegalStateException.class) .hasMessage( "Can not access field, a public field or accessor is required.Declaring class is org.apache.servicecomb.foundation.common.utils.TestLambdaMetafactoryUtils$Model, field is f2"); assertThat(catchThrowable(() -> LambdaMetafactoryUtils.createSetter(field))) .isInstanceOf(IllegalStateException.class) .hasMessage( "Can not access field, a public field or accessor is required.Declaring class is org.apache.servicecomb.foundation.common.utils.TestLambdaMetafactoryUtils$Model, field is f2"); } public static class Base { private T base; public T getBase() { return base; } public Base setBase(T base) { this.base = base; return this; } } public static class Child extends Base { private int child; public int getChild() { return child; } public Child setChild(int child) { this.child = child; return this; } } @Test public void should_support_primitive_type() { Child child = new Child(); ObjectMapper mapper = JsonUtils.OBJ_MAPPER; BeanDescription beanDescription = mapper.getSerializationConfig().introspect(mapper.constructType(Child.class)); List properties = beanDescription.findProperties(); assertThat(properties).hasSize(2); for (int idx = 0; idx < properties.size(); idx++) { BeanPropertyDefinition property = properties.get(idx); Setter setter = createObjectSetter(property.getSetter().getAnnotated()); setter.set(child, idx); Getter getter = createObjectGetter(property.getGetter().getAnnotated()); Object value = getter.get(child); assertThat(value).isEqualTo(idx); } } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaPerformance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.servicecomb.foundation.common.utils.TestLambdaMetafactoryUtils.Model; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; public class TestLambdaPerformance { static Model model = new Model(); // 20 field/msg, 10_0000tps static int count = 20 * 100_0000; static int sum = 0; static Method f1_getter; static MethodHandle mh_f1_getter; static IntGetter lambda_f1_method_getter; static Field f1_field; static Getter lambda_f1_field_getter; static { model.setF1(123456); try { f1_field = Model.class.getDeclaredField("f1"); f1_field.setAccessible(true); f1_getter = Model.class.getMethod("getF1"); mh_f1_getter = MethodHandles.lookup().unreflect(f1_getter); lambda_f1_method_getter = LambdaMetafactoryUtils.createGetter(f1_getter); lambda_f1_field_getter = LambdaMetafactoryUtils.createGetter(f1_field); } catch (Throwable throwable) { throwable.printStackTrace(); } } public static long directGetter() { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += model.getF1(); } return System.nanoTime() - start; } public static long reflectfieldF1() throws IllegalAccessException { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += (int) f1_field.get(model); } return System.nanoTime() - start; } public static long reflectIntFieldF1() throws IllegalAccessException { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += f1_field.getInt(model); } return System.nanoTime() - start; } public static long lambdaMethodGetter() throws Throwable { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += lambda_f1_method_getter.get(model); } return System.nanoTime() - start; } public static long lambdaFieldGetter() throws Throwable { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += lambda_f1_field_getter.get(model); } return System.nanoTime() - start; } public static long mhGetterInvoke() throws Throwable { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += (int) mh_f1_getter.invoke(model); } return System.nanoTime() - start; } public static long mhGetterExact() throws Throwable { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += (int) mh_f1_getter.invokeExact(model); } return System.nanoTime() - start; } public static long reflectGetter() throws InvocationTargetException, IllegalAccessException { long start = System.nanoTime(); sum = 0; for (int idx = 0; idx < count; idx++) { sum += (int) f1_getter.invoke(model); } return System.nanoTime() - start; } public static void main(String[] args) throws Throwable { lambdaMethodGetter(); directGetter(); mhGetterInvoke(); reflectGetter(); mhGetterExact(); reflectfieldF1(); reflectIntFieldF1(); System.out.println("mhGetterInvoke : " + mhGetterInvoke()); System.out.println("mhGetterExact : " + mhGetterExact()); System.out.println("reflectGetter : " + reflectGetter()); System.out.println(""); System.out.println("reflectfieldF1 : " + reflectfieldF1()); System.out.println("lambdaFieldGetter : " + lambdaFieldGetter()); System.out.println(""); System.out.println("lambdaMethodGetter : " + lambdaMethodGetter()); System.out.println("directGetter : " + directGetter()); System.out.println("reflectIntFieldF1 : " + reflectIntFieldF1()); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestMimeTypesUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMimeTypesUtils { @Test public void testSortedAcceptableMimeTypes1() { String accept = "text/html"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(1, types.size()); Assertions.assertEquals("text/html", types.get(0)); } @Test public void testSortedAcceptableMimeTypes2() { String accept = "text/html, application/json"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(2, types.size()); Assertions.assertEquals("text/html", types.get(0)); Assertions.assertEquals("application/json", types.get(1)); } @Test public void testSortedAcceptableMimeTypes3() { String accept = "text/html,application/json"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(2, types.size()); Assertions.assertEquals("text/html", types.get(0)); Assertions.assertEquals("application/json", types.get(1)); } @Test public void testSortedAcceptableMimeTypes4() { String accept = "text/html; q=0.8,application/json; q=0.9"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(2, types.size()); Assertions.assertEquals("application/json", types.get(0)); Assertions.assertEquals("text/html", types.get(1)); } @Test public void testSortedAcceptableMimeTypes5() { String accept = "text/html;q=0.8,application/json;q=0.9"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(2, types.size()); Assertions.assertEquals("application/json", types.get(0)); Assertions.assertEquals("text/html", types.get(1)); } @Test public void testSortedAcceptableMimeTypes6() { String accept = "text/html; q=0.8,application/json; q=0.9, text/plain"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(3, types.size()); Assertions.assertEquals("text/plain", types.get(0)); Assertions.assertEquals("application/json", types.get(1)); Assertions.assertEquals("text/html", types.get(2)); } @Test public void testSortedAcceptableMimeTypes7() { String accept = "text/html;q=0.8,application/json;q=0.9,text/plain"; List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); Assertions.assertEquals(3, types.size()); Assertions.assertEquals("text/plain", types.get(0)); Assertions.assertEquals("application/json", types.get(1)); Assertions.assertEquals("text/html", types.get(2)); } @Test public void getSortedAcceptableMimeTypesNull() { List types = MimeTypesUtils.getSortedAcceptableMimeTypes(null); Assertions.assertSame(Collections.emptyList(), types); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestRSAUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestRSAUtil { @BeforeAll public static void setUpClass() { Environment environment = Mockito.mock(Environment.class); LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) .thenReturn("RSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) .thenReturn("SHA256withRSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) .thenReturn(2048); } @Test public void testSignVerify() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { KeyPairEntry keyPairEntry = KeyPairUtils.generateALGKeyPair(); Assertions.assertNotNull(keyPairEntry.getPublicKeyEncoded()); Assertions.assertNotNull(keyPairEntry.getPrivateKey()); Assertions.assertNotNull(keyPairEntry.getPublicKey()); String testContent = "instance-id@201711201930@randomstr"; String signstr = KeyPairUtils.sign(testContent, keyPairEntry.getPrivateKey()); Assertions.assertTrue(KeyPairUtils.verify(keyPairEntry.getPublicKeyEncoded(), signstr, testContent)); } @Test public void testSignVerify2() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { String sign = "WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; String content = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ"; String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"; Assertions.assertTrue(KeyPairUtils.verify(pubKey, sign, content)); } } ================================================ FILE: foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestTypesUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTypesUtil { @Test public void testTypesUtil() { Assertions.assertEquals(double.class, TypesUtil.wrapperTypeToPrimitive(Double.class)); Assertions.assertEquals(Float.class, TypesUtil.primitiveTypeToWrapper(float.class)); Assertions .assertEquals(TypesUtil.PRIMITIVE_CHAR, TypesUtil.wrapperJavaTypeToPrimitive(TypesUtil.PRIMITIVE_WRAPPER_CHAR)); Assertions .assertEquals(TypesUtil.PRIMITIVE_WRAPPER_BYTE, TypesUtil.primitiveJavaTypeToWrapper(TypesUtil.PRIMITIVE_BYTE)); } } ================================================ FILE: foundations/foundation-common/src/test/resources/META-INF/spring/config.bean.xml ================================================ ${1.2} ================================================ FILE: foundations/foundation-common/src/test/resources/log4j2.xml ================================================ ================================================ FILE: foundations/foundation-config/pom.xml ================================================ foundations org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 foundation-config Java Chassis::Foundations::Config org.apache.servicecomb foundation-common org.springframework spring-beans org.springframework.boot spring-boot-autoconfigure org.yaml snakeyaml jakarta.annotation jakarta.annotation-api org.slf4j slf4j-api org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding org.apache.httpcomponents httpclient commons-logging commons-logging io.netty netty-codec io.netty netty-codec-http io.netty netty-codec-http2 io.netty netty-common io.netty netty-handler io.netty netty-buffer io.netty netty-codec-socks io.netty netty-handler-proxy io.netty netty-resolver io.netty netty-transport com.google.guava guava org.jmockit jmockit test ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/BootStrapProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.springframework.core.env.Environment; /** * This class holds configurations that need to be configured * through property files or environment variables. * * These properties are core model for java chassis, e.g. *
 *   servicecomb:
 *     service:
 *       application:
 *       name:
 *       version:
 *       environment:
 *       properties:
 * 
* */ public class BootStrapProperties { // start of : service definition keys // service definition keys of old version public static final String OLD_CONFIG_SERVICE_APPLICATION = "APPLICATION_ID"; public static final String OLD_CONFIG_SERVICE_NAME = "service_description.name"; public static final String OLD_CONFIG_SERVICE_VERSION = "service_description.version"; public static final String OLD_CONFIG_SERVICE_ROLE = "service_description.role"; public static final String OLD_CONFIG_SERVICE_ALIAS = "service_description.alias"; public static final String OLD_CONFIG_SERVICE_DESCRIPTION = "service_description.description"; public static final String OLD_CONFIG_SERVICE_ENVIRONMENT = "service_description.environment"; public static final String OLD_CONFIG_SERVICE_EXTENDED_CLASS = "service_description.propertyExtendedClass"; public static final String OLD_CONFIG_SERVICE_PROPERTIES = "service_description.properties"; // service instance definition keys of old version public static final String OLD_CONFIG_SERVICE_INSTANCE_PROPERTIES = "instance_description.properties"; public static final String OLD_CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS = "instance_description.propertyExtendedClass"; public static final String OLD_CONFIG_SERVICE_INSTANCE_INITIAL_STATUS = "instance_description.initialStatus"; // service definition keys of new version public static final String CONFIG_SERVICE_APPLICATION = "servicecomb.service.application"; public static final String CONFIG_SERVICE_NAME = "servicecomb.service.name"; public static final String CONFIG_SERVICE_VERSION = "servicecomb.service.version"; public static final String CONFIG_SERVICE_ROLE = "servicecomb.service.role"; public static final String CONFIG_SERVICE_ALIAS = "servicecomb.service.alias"; public static final String CONFIG_SERVICE_DESCRIPTION = "servicecomb.service.description"; public static final String CONFIG_SERVICE_ENVIRONMENT = "servicecomb.service.environment"; public static final String CONFIG_SERVICE_EXTENDED_CLASS = "servicecomb.service.propertyExtendedClass"; public static final String CONFIG_SERVICE_PROPERTIES = "servicecomb.service.properties"; // service instance definition keys of new version public static final String CONFIG_SERVICE_INSTANCE_PROPERTIES = "servicecomb.instance.properties"; public static final String CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS = "servicecomb.instance.propertyExtendedClass"; public static final String CONFIG_SERVICE_INSTANCE_INITIAL_STATUS = "servicecomb.instance.initialStatus"; // configuration default values public static final String DEFAULT_APPLICATION = "default"; public static final String DEFAULT_MICROSERVICE_NAME = "defaultMicroservice"; public static final String DEFAULT_MICROSERVICE_VERSION = "1.0.0.0"; public static final String DEFAULT_MICROSERVICE_ROLE = "FRONT"; public static final String DEFAULT_MICROSERVICE_ENVIRONMENT = ""; public static final String DEFAULT_MICROSERVICE_INSTANCE_INITIAL_STATUS = "UP"; public static String readApplication(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_APPLICATION, OLD_CONFIG_SERVICE_APPLICATION, DEFAULT_APPLICATION); } public static String readServiceName(Environment environment) { String result = readStringValue(environment, CONFIG_SERVICE_NAME, OLD_CONFIG_SERVICE_NAME, DEFAULT_MICROSERVICE_NAME); checkMicroserviceName(result); return result; } public static String readServiceVersion(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_VERSION, OLD_CONFIG_SERVICE_VERSION, DEFAULT_MICROSERVICE_VERSION); } public static String readServiceRole(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_ROLE, OLD_CONFIG_SERVICE_ROLE, DEFAULT_MICROSERVICE_ROLE); } public static String readServiceAlias(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_ALIAS, OLD_CONFIG_SERVICE_ALIAS, null); } public static String readServiceDescription(Environment environment) { String[] descriptionArray = environment.getProperty(CONFIG_SERVICE_DESCRIPTION, String[].class); if (null == descriptionArray || descriptionArray.length < 1) { descriptionArray = environment.getProperty(OLD_CONFIG_SERVICE_DESCRIPTION, String[].class); } if (null == descriptionArray || descriptionArray.length < 1) { return null; } StringBuilder rawDescriptionBuilder = new StringBuilder(); for (String desc : descriptionArray) { rawDescriptionBuilder.append(desc).append(","); } return rawDescriptionBuilder.substring(0, rawDescriptionBuilder.length() - 1); } public static String readServiceEnvironment(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_ENVIRONMENT, OLD_CONFIG_SERVICE_ENVIRONMENT, DEFAULT_MICROSERVICE_ENVIRONMENT); } public static String readServiceExtendedClass(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_EXTENDED_CLASS, OLD_CONFIG_SERVICE_EXTENDED_CLASS, null); } public static Map readServiceProperties(Environment environment) { return readProperties(environment, CONFIG_SERVICE_PROPERTIES, OLD_CONFIG_SERVICE_PROPERTIES); } public static Map readServiceInstanceProperties(Environment environment) { return readProperties(environment, CONFIG_SERVICE_INSTANCE_PROPERTIES, OLD_CONFIG_SERVICE_INSTANCE_PROPERTIES); } public static String readServiceInstanceExtendedClass(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, OLD_CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, null); } public static String readServiceInstanceInitialStatus(Environment environment) { return readStringValue(environment, CONFIG_SERVICE_INSTANCE_INITIAL_STATUS, OLD_CONFIG_SERVICE_INSTANCE_INITIAL_STATUS, DEFAULT_MICROSERVICE_INSTANCE_INITIAL_STATUS); } private static String readStringValue(Environment environment, String newKey, String oldKey, String defaultValue) { String result = environment.getProperty(newKey); if (result == null) { return environment.getProperty(oldKey, defaultValue); } return result; } private static Map readProperties(Environment environment, String newKey, String oldKey) { String prefix = newKey; Set keys = ConfigUtil.propertiesWithPrefix(environment, prefix); if (keys.isEmpty()) { prefix = oldKey; keys = ConfigUtil.propertiesWithPrefix(environment, oldKey); } Map result = new HashMap<>(keys.size()); for (String key : keys) { result.put(key.substring(prefix.length() + 1), environment.getProperty(key)); } return result; } private static void checkMicroserviceName(String name) { // the configuration we used // when resolve placeholder failed // the result will remains ${var} if (StringUtils.isEmpty(name) || name.contains("${")) { throw new IllegalArgumentException(String.format( "MicroserviceName '%s' is invalid. you must configure '%s' or set the placeholder value.", name, CONFIG_SERVICE_NAME)); } } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.servicecomb.config.file.MicroserviceConfigLoader; import org.apache.servicecomb.foundation.bootstrap.BootStrapService; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.context.annotation.Conditional; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; /** * Initialize configuration. */ public class ConfigEnvironmentPostProcessor implements EnvironmentPostProcessor { public static final String MICROSERVICE_PROPERTY_SOURCE_NAME = "microservice.yaml"; public static final String MAPPING_PROPERTY_SOURCE_NAME = "mapping.yaml"; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { LegacyPropertyFactory.setEnvironment(environment); addMicroserviceDefinitions(environment); startupBootStrapService(environment); addDynamicConfigurationToSpring(environment); } public static void addMicroserviceDefinitions(Environment environment) { addMicroserviceYAMLToSpring(environment); addMappingToSpring(environment); } private void startupBootStrapService(Environment environment) { for (BootStrapService bootStrapService : SPIServiceUtils.getSortedService(BootStrapService.class)) { bootStrapService.startup(environment); } } /** * make springboot have a change to add microservice.yaml source earlier
* to affect {@link Conditional} * @param environment environment */ private static void addMicroserviceYAMLToSpring(Environment environment) { if (!(environment instanceof ConfigurableEnvironment)) { return; } MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); if (propertySources.contains(MICROSERVICE_PROPERTY_SOURCE_NAME)) { return; } propertySources.addLast(new EnumerablePropertySource(MICROSERVICE_PROPERTY_SOURCE_NAME) { private final Map values = new HashMap<>(); private final String[] propertyNames; { MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); loader.loadAndSort(); loader.getConfigModels() .forEach(configModel -> values.putAll(YAMLUtil.retrieveItems("", configModel.getConfig()))); propertyNames = values.keySet().toArray(new String[0]); } @Override public String[] getPropertyNames() { return propertyNames; } @SuppressWarnings("unchecked") @Override public Object getProperty(String name) { Object value = this.values.get(name); // spring will not resolve nested placeholder of list, so try to fix the problem if (value instanceof List) { value = ((List) value).stream() .filter(item -> item instanceof String) .map(item -> environment.resolvePlaceholders((String) item)) .collect(Collectors.toList()); } return value; } }); } private static void addMappingToSpring(Environment environment) { if (!(environment instanceof ConfigurableEnvironment)) { return; } MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources(); if (propertySources.contains(MAPPING_PROPERTY_SOURCE_NAME)) { return; } Map mappings = ConfigMapping.getConvertedMap(environment); propertySources.addFirst(new MapPropertySource(MAPPING_PROPERTY_SOURCE_NAME, mappings)); } private void addDynamicConfigurationToSpring(Environment environment) { if (!(environment instanceof ConfigurableEnvironment)) { return; } for (DynamicPropertiesSource dynamicPropertiesSource : SPIServiceUtils.getOrLoadSortedService(DynamicPropertiesSource.class)) { ((ConfigurableEnvironment) environment).getPropertySources() .addFirst(dynamicPropertiesSource.create(environment)); } } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigMapping.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.file.AbstractConfigLoader; import org.apache.servicecomb.foundation.common.utils.JvmUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; /** * Created by on 2017/1/5. */ public final class ConfigMapping { private static final Map configMap; private static final Logger LOGGER = LoggerFactory.getLogger(ConfigMapping.class); static { ClassLoader loader = JvmUtils.findClassLoader(); List> mappingList = new ArrayList<>(); configMap = new HashMap<>(); Enumeration urls; try { urls = loader.getResources("mapping.yaml"); while (urls.hasMoreElements()) { try (InputStream in = urls.nextElement().openStream()) { mappingList.add(YAMLUtil.yaml2Properties(in)); } } mappingList.sort((a, b) -> { int orderA = a.get(AbstractConfigLoader.ORDER_KEY) == null ? 0 : (int) a.get(AbstractConfigLoader.ORDER_KEY); int orderB = b.get(AbstractConfigLoader.ORDER_KEY) == null ? 0 : (int) b.get(AbstractConfigLoader.ORDER_KEY); return orderA - orderB; }); mappingList.forEach(item -> { item.remove(AbstractConfigLoader.ORDER_KEY); configMap.putAll(item); }); } catch (IOException e) { LOGGER.error("get config mapping file error!", e); } } private ConfigMapping() { } @VisibleForTesting @SuppressWarnings("unchecked") static T map(String key) { return (T) configMap.get(key); } public static Map getMapping() { return configMap; } public static Map getConvertedMap(Map oldMap) { if (configMap == null) { return new LinkedHashMap<>(); } Map retMap = new LinkedHashMap<>(); retMap.putAll(oldMap); configMap.entrySet().forEach(entry -> putConfigsToRetMap(retMap, entry, oldMap.get(entry.getKey()))); return retMap; } public static Map getConvertedMap(Environment environment) { if (configMap == null) { return new LinkedHashMap<>(); } Map retMap = new LinkedHashMap<>(); configMap.entrySet().forEach(entry -> putConfigsToRetMap(retMap, entry, environment.getProperty(entry.getKey()))); return retMap; } private static void putConfigsToRetMap(Map retMap, Map.Entry entry, Object configValue) { if (configValue != null) { if (entry.getValue() instanceof List) { @SuppressWarnings("unchecked") List newKeys = (List) entry.getValue(); newKeys.forEach(newKey -> retMap.put(newKey, configValue)); return; } String newKey = (String) entry.getValue(); retMap.put(newKey, configValue); } } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; public final class ConfigUtil { private ConfigUtil() { } public static List parseArrayValue(String value) { if (value == null) { return new ArrayList<>(0); } String[] tokens = value.split(","); List result = new ArrayList<>(tokens.length); for (String t : tokens) { result.add(t.trim()); } return result; } public static Set propertiesWithPrefix(Environment environment, String prefix) { Set result = new HashSet<>(); for (PropertySource propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { if (key.startsWith(prefix)) { result.add(key); } } } } return result; } public static Map stringPropertiesWithPrefix(Environment environment, String prefix) { Map result = new HashMap<>(); for (PropertySource propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) { if (key.startsWith(prefix)) { result.put(key, environment.getProperty(key)); } } } } return result; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigurationChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; /** * This event is fired when configuration changed. And the change is already applied to Environment. * * Listeners can use Environment to get the latest value. */ public class ConfigurationChangedEvent { private final Map added; private final Map deleted; private final Map updated; private Set changed; private ConfigurationChangedEvent(Map added, Map updated, Map deleted) { this.added = added; this.deleted = deleted; this.updated = updated; this.changed = new HashSet<>(); this.changed.addAll(added.keySet()); this.changed.addAll(updated.keySet()); this.changed.addAll(deleted.keySet()); } public static ConfigurationChangedEvent createIncremental(Map latest, Map last) { Map itemsCreated = new HashMap<>(); Map itemsDeleted = new HashMap<>(); Map itemsModified = new HashMap<>(); for (Map.Entry entry : latest.entrySet()) { String itemKey = entry.getKey(); if (!last.containsKey(itemKey)) { itemsCreated.put(itemKey, entry.getValue()); } else if (!Objects.equals(last.get(itemKey), latest.get(itemKey))) { itemsModified.put(itemKey, entry.getValue()); } } for (String itemKey : last.keySet()) { if (!latest.containsKey(itemKey)) { itemsDeleted.put(itemKey, null); } } ConfigurationChangedEvent event = ConfigurationChangedEvent .createIncremental(itemsCreated, itemsModified, itemsDeleted); return event; } public static ConfigurationChangedEvent createIncremental(Map added, Map updated, Map deleted) { return new ConfigurationChangedEvent(added, updated, deleted); } public static ConfigurationChangedEvent createIncremental(Map updated) { return new ConfigurationChangedEvent(new HashMap<>(), updated, new HashMap<>()); } public final Map getAdded() { return added; } public final Map getUpdated() { return updated; } public final Map getDeleted() { return deleted; } public final Set getChanged() { return changed; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DataCenterProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; public class DataCenterProperties { public static final String PREFIX = "servicecomb.datacenter"; private String name; private String region; private String availableZone; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRegion() { return region; } public void setRegion(String region) { this.region = region; } public String getAvailableZone() { return availableZone; } public void setAvailableZone(String availableZone) { this.availableZone = availableZone; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; import java.util.function.LongConsumer; public interface DynamicProperties { default String getStringProperty(String propertyName, Consumer consumer, String defaultValue) { return defaultValue; } default String getStringProperty(String propertyName, String defaultValue) { return defaultValue; } default int getIntProperty(String propertyName, IntConsumer consumer, int defaultValue) { return defaultValue; } default int getIntProperty(String propertyName, int defaultValue) { return defaultValue; } default long getLongProperty(String propertyName, LongConsumer consumer, long defaultValue) { return defaultValue; } default long getLongProperty(String propertyName, long defaultValue) { return defaultValue; } default float getFloatProperty(String propertyName, DoubleConsumer consumer, float defaultValue) { return defaultValue; } default float getFloatProperty(String propertyName, float defaultValue) { return defaultValue; } default double getDoubleProperty(String propertyName, DoubleConsumer consumer, double defaultValue) { return defaultValue; } default double getDoubleProperty(String propertyName, double defaultValue) { return defaultValue; } default boolean getBooleanProperty(String propertyName, Consumer consumer, boolean defaultValue) { return defaultValue; } default boolean getBooleanProperty(String propertyName, boolean defaultValue) { return defaultValue; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; import java.util.function.LongConsumer; import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; public class DynamicPropertiesImpl implements DynamicProperties { private static class Holder { C callback; D defaultValue; Holder(C callback, D defaultValue) { this.callback = callback; this.defaultValue = defaultValue; } } private final Map, String>>> stringCallbacks = new ConcurrentHashMap<>(); private final Map>> intCallbacks = new ConcurrentHashMap<>(); private final Map>> longCallbacks = new ConcurrentHashMap<>(); private final Map>> floatCallbacks = new ConcurrentHashMap<>(); private final Map>> doubleCallbacks = new ConcurrentHashMap<>(); private final Map, Boolean>>> booleanCallbacks = new ConcurrentHashMap<>(); private final Environment environment; public DynamicPropertiesImpl(Environment environment) { this.environment = environment; EventManager.register(this); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (Entry entry : event.getAdded().entrySet()) { updateValue(entry); } for (Entry entry : event.getUpdated().entrySet()) { updateValue(entry); } for (Entry entry : event.getDeleted().entrySet()) { updateValue(entry); } } private void updateValue(Entry entry) { if (stringCallbacks.containsKey(entry.getKey())) { for (Holder, String> callbacks : stringCallbacks.get(entry.getKey())) { callbacks.callback.accept(environment.getProperty(entry.getKey(), callbacks.defaultValue)); } } if (intCallbacks.containsKey(entry.getKey())) { for (Holder callbacks : intCallbacks.get(entry.getKey())) { callbacks.callback.accept(environment.getProperty(entry.getKey(), Integer.class, callbacks.defaultValue)); } } if (longCallbacks.containsKey(entry.getKey())) { for (Holder callbacks : longCallbacks.get(entry.getKey())) { callbacks.callback.accept(environment.getProperty(entry.getKey(), Long.class, callbacks.defaultValue)); } } if (floatCallbacks.containsKey(entry.getKey())) { for (Holder callbacks : floatCallbacks.get(entry.getKey())) { callbacks.callback.accept(environment.getProperty(entry.getKey(), Float.class, callbacks.defaultValue)); } } if (doubleCallbacks.containsKey(entry.getKey())) { for (Holder callbacks : doubleCallbacks.get(entry.getKey())) { callbacks.callback.accept(environment.getProperty(entry.getKey(), Double.class, callbacks.defaultValue)); } } if (booleanCallbacks.containsKey(entry.getKey())) { for (Holder, Boolean> callbacks : booleanCallbacks.get(entry.getKey())) { callbacks.callback.accept(environment.getProperty(entry.getKey(), Boolean.class, callbacks.defaultValue)); } } } @Override public String getStringProperty(String propertyName, Consumer consumer, String defaultValue) { stringCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); return environment.getProperty(propertyName, defaultValue); } @Override public String getStringProperty(String propertyName, String defaultValue) { return environment.getProperty(propertyName, defaultValue); } @Override public int getIntProperty(String propertyName, IntConsumer consumer, int defaultValue) { intCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); return environment.getProperty(propertyName, int.class, defaultValue); } @Override public int getIntProperty(String propertyName, int defaultValue) { return environment.getProperty(propertyName, int.class, defaultValue); } @Override public long getLongProperty(String propertyName, LongConsumer consumer, long defaultValue) { longCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); return environment.getProperty(propertyName, long.class, defaultValue); } @Override public long getLongProperty(String propertyName, long defaultValue) { return environment.getProperty(propertyName, long.class, defaultValue); } @Override public float getFloatProperty(String propertyName, DoubleConsumer consumer, float defaultValue) { floatCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); return environment.getProperty(propertyName, float.class, defaultValue); } @Override public float getFloatProperty(String propertyName, float defaultValue) { return environment.getProperty(propertyName, float.class, defaultValue); } @Override public double getDoubleProperty(String propertyName, DoubleConsumer consumer, double defaultValue) { doubleCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); return environment.getProperty(propertyName, double.class, defaultValue); } @Override public double getDoubleProperty(String propertyName, double defaultValue) { return environment.getProperty(propertyName, double.class, defaultValue); } @Override public boolean getBooleanProperty(String propertyName, Consumer consumer, boolean defaultValue) { booleanCallbacks.computeIfAbsent(propertyName, key -> new HashSet<>()).add(new Holder<>(consumer, defaultValue)); return environment.getProperty(propertyName, boolean.class, defaultValue); } @Override public boolean getBooleanProperty(String propertyName, boolean defaultValue) { return environment.getProperty(propertyName, boolean.class, defaultValue); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import org.springframework.core.Ordered; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; /** * SPI to add new DynamicPropertiesSource. */ public interface DynamicPropertiesSource extends Ordered { PropertySource create(Environment environment); } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import org.apache.servicecomb.config.inject.InjectBeanPostProcessor; import org.apache.servicecomb.config.priority.ConfigObjectFactory; import org.apache.servicecomb.config.priority.PriorityPropertyFactory; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; @Configuration @SuppressWarnings("unused") public class FoundationConfigConfiguration { @Bean public static InjectBeanPostProcessor scbInjectBeanPostProcessor( @Autowired @Lazy PriorityPropertyManager priorityPropertyManager) { return new InjectBeanPostProcessor(priorityPropertyManager); } @Bean public PriorityPropertyManager scbPriorityPropertyManager(ConfigObjectFactory configObjectFactory) { return new PriorityPropertyManager(configObjectFactory); } @Bean public PriorityPropertyFactory scbPriorityPropertyFactory(Environment environment) { return new PriorityPropertyFactory(environment); } @Bean public DynamicPropertiesImpl scbDynamicProperties(Environment environment) { return new DynamicPropertiesImpl(environment); } @Bean public ConfigObjectFactory scbConfigObjectFactory(PriorityPropertyFactory propertyFactory) { return new ConfigObjectFactory(propertyFactory); } @Bean @ConfigurationProperties(prefix = DataCenterProperties.PREFIX) public DataCenterProperties scbDataCenterProperties() { return new DataCenterProperties(); } @Bean public InMemoryDynamicPropertiesSource scbInMemoryDynamicPropertiesSource() { return new InMemoryDynamicPropertiesSource(); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; /** * This DynamicPropertiesSource is created for easier system tests. */ public class InMemoryDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "in-memory"; private static final Map DYNAMIC = new HashMap<>(); @Override public PropertySource create(Environment environment) { return new MapPropertySource(SOURCE_NAME, DYNAMIC); } @Override public int getOrder() { return -100; } public static void update(String key, Object value) { DYNAMIC.put(key, value); HashMap updated = new HashMap<>(); updated.put(key, value); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); } public static void reset() { DYNAMIC.clear(); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.io.InputStream; import java.util.LinkedHashMap; import java.util.Map; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.TypeDescription; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; public final class YAMLUtil { private YAMLUtil() { } private static Yaml safeParser() { // Yaml instance is not thread safe, create a new instance for each parser return new Yaml(new SafeConstructor(new LoaderOptions())); } /** * load a input {@link InputStream} to be a map {@link Map}, you have to close the inputStream by yourself, such as:
*

try (InputStream in = url.openStream()) {
*      configMap.putAll(YAMLUtil.yaml2Properties(in));
* }
*

* @param input the stream to be loaded * @return a config map */ @SuppressWarnings("unchecked") public static Map yaml2Properties(InputStream input) { Map configurations = new LinkedHashMap<>(); safeParser().loadAll(input).forEach(data -> { if (data instanceof Map && isValidMap((Map) data)) { configurations.putAll(retrieveItems("", (Map) data)); } else { throw new IllegalArgumentException("input cannot be convert to map"); } }); return configurations; } @SuppressWarnings("unchecked") private static boolean isValidMap(Map data) { for (Map.Entry entry : data.entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); if (key instanceof String) { if (value instanceof Map) { return isValidMap((Map) value); } continue; } return false; } return true; } /** * load a input {@link String} to be a map {@link Map} * @param input the String to be loaded * @return a config map */ @SuppressWarnings("unchecked") public static Map yaml2Properties(String input) { Map configurations = new LinkedHashMap<>(); safeParser().loadAll(input).forEach(data -> { if (data instanceof Map && isValidMap((Map) data)) { configurations.putAll(retrieveItems("", (Map) data)); } else { throw new IllegalArgumentException("input cannot be convert to map"); } }); return configurations; } public static T parserObject(String yamlContent, Class clazz) { Yaml parser = new Yaml(new Constructor(new TypeDescription(clazz, clazz), new LoaderOptions())); return parser.loadAs(yamlContent, clazz); } @SuppressWarnings("unchecked") public static Map retrieveItems(String prefix, Map propertiesMap) { Map result = new LinkedHashMap<>(); if (!prefix.isEmpty()) { prefix += "."; } for (Map.Entry entry : propertiesMap.entrySet()) { if (entry.getValue() instanceof Map) { result.putAll(retrieveItems(prefix + entry.getKey(), (Map) entry.getValue())); } else { String key = prefix + entry.getKey(); result.put(key, entry.getValue()); } } return result; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.file; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.JvmUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ResourceUtils; public abstract class AbstractConfigLoader { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractConfigLoader.class); public static final String ORDER_KEY = "servicecomb-config-order"; protected final List configModels = new ArrayList<>(); public List getConfigModels() { return configModels; } public void load(String resourceName) throws IOException { loadFromClassPath(resourceName); } protected void loadFromClassPath(String resourceName) throws IOException { List urlList = findURLFromClassPath(resourceName); for (URL url : urlList) { ConfigModel configModel = load(url); configModels.add(configModel); } } public ConfigModel load(URL url) throws IOException { Map config = loadData(url); // load a empty or all commented yaml, will get a null map // this is not an error if (config == null) { config = new LinkedHashMap<>(); } ConfigModel configModel = new ConfigModel(); configModel.setUrl(url); configModel.setConfig(config); Object objOrder = config.get(ORDER_KEY); if (objOrder == null) { // compatible check objOrder = config.get("cse-config-order"); if (objOrder != null) { LOGGER.error("cse-config-order will not be supported in future, please change it to servicecomb-config-order"); } } if (objOrder != null) { if (Integer.class.isInstance(objOrder)) { configModel.setOrder((int) objOrder); } else { configModel.setOrder(Integer.parseInt(String.valueOf(objOrder))); } } return configModel; } protected abstract Map loadData(URL url) throws IOException; protected List findURLFromClassPath(String resourceName) throws IOException { List urlList = new ArrayList<>(); ClassLoader loader = JvmUtils.findClassLoader(); Enumeration urls = loader.getResources(resourceName); while (urls.hasMoreElements()) { urlList.add(urls.nextElement()); } return urlList; } private static class ConfigModelWrapper { ConfigModel model; int addOrder; } // sort rule: // 1.files in jar // 2.smaller order // 3.add to list earlier protected void sort() { List list = new ArrayList<>(configModels.size()); for (int idx = 0; idx < configModels.size(); idx++) { ConfigModelWrapper wrapper = new ConfigModelWrapper(); wrapper.model = configModels.get(idx); wrapper.addOrder = idx; list.add(wrapper); } list.sort(this::doSort); for (int idx = 0; idx < configModels.size(); idx++) { configModels.set(idx, list.get(idx).model); } } private int doSort(ConfigModelWrapper w1, ConfigModelWrapper w2) { ConfigModel m1 = w1.model; ConfigModel m2 = w2.model; boolean isM1Jar = ResourceUtils.isJarURL(m1.getUrl()); boolean isM2Jar = ResourceUtils.isJarURL(m2.getUrl()); if (isM1Jar != isM2Jar) { if (isM1Jar) { return -1; } return 1; } // min order load first int result = Integer.compare(m1.getOrder(), m2.getOrder()); if (result != 0) { return result; } return doFinalSort(w1, w2); } private int doFinalSort(ConfigModelWrapper w1, ConfigModelWrapper w2) { return Integer.compare(w1.addOrder, w2.addOrder); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.file; import java.net.URL; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.IOUtils; public class ConfigModel { private URL url; private int order; private Map config; public URL getUrl() { return url; } public void setUrl(URL url) { this.url = url; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public Map getConfig() { return config; } public void setConfig(Map config) { this.config = config; } @Override public String toString() { return url == null ? "" : IOUtils.anonymousPath(url.toString()); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.file; import java.io.IOException; import java.net.URL; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MicroserviceConfigLoader extends YAMLConfigLoader { private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceConfigLoader.class); private static final String ADDITIONAL_CONFIG_URL = "servicecomb.configurationSource.additionalUrls"; private static final String DEFAULT_FILE_NAME = "servicecomb.configurationSource.defaultFileName"; /** * Default configuration file name to be used by default constructor. This file should * be on the classpath. The file name can be overridden by the value of system property * configurationSource.defaultFileName */ private static final String DEFAULT_CONFIG_FILE_NAME = "microservice.yaml"; public MicroserviceConfigLoader() { // Help to resolve incompatible changes. Can be deleted in future. if (!StringUtils.isEmpty(System.getProperty("cse.configurationSource.additionalUrls"))) { throw new IllegalArgumentException("-Dcse.configurationSource.additionalUrls" + " has been replaced with -D" + ADDITIONAL_CONFIG_URL + ", please change it and restart."); } if (!StringUtils.isEmpty(System.getProperty("cse.configurationSource.defaultFileName"))) { throw new IllegalArgumentException("-Dcse.configurationSource.additionalUrls" + " has been replaced with -D" + DEFAULT_FILE_NAME + ", please change it and restart."); } } public void loadAndSort() { String configFileFromClasspath = null; try { configFileFromClasspath = System.getProperty(DEFAULT_FILE_NAME) == null ? DEFAULT_CONFIG_FILE_NAME : System.getProperty(DEFAULT_FILE_NAME); super.load(configFileFromClasspath); loadAdditionalConfig(); if (configModels.isEmpty()) { LOGGER.warn("No URLs will be polled as dynamic configuration sources."); LOGGER.warn( "To enable URLs as dynamic configuration sources, define System property {} or make {} available on classpath.", ADDITIONAL_CONFIG_URL, configFileFromClasspath); } sort(); } catch (Exception e) { throw new ServiceCombException("Failed to load microservice configFile " + configFileFromClasspath, e); } } private void loadAdditionalConfig() throws IOException { String strUrls = System.getProperty(ADDITIONAL_CONFIG_URL); if (StringUtils.isEmpty(strUrls)) { return; } for (String strUrl : strUrls.split(",")) { URL url = new URL(strUrl); ConfigModel configModel = load(url); configModels.add(configModel); } } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.file; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Map; import org.apache.servicecomb.config.YAMLUtil; public class YAMLConfigLoader extends AbstractConfigLoader { @Override protected Map loadData(URL url) throws IOException { try (InputStream inputStream = url.openStream()) { return YAMLUtil.yaml2Properties(inputStream); } } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectBeanPostProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.inject; import java.util.Collections; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class InjectBeanPostProcessor implements BeanPostProcessor { private final PriorityPropertyManager priorityPropertyManager; public InjectBeanPostProcessor(PriorityPropertyManager priorityPropertyManager) { this.priorityPropertyManager = priorityPropertyManager; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class beanCls = BeanUtils.getImplClassFromBean(bean); InjectProperties injectProperties = beanCls.getAnnotation(InjectProperties.class); if (injectProperties == null) { return bean; } priorityPropertyManager.createConfigObject(bean, Collections.emptyMap()); return bean; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.inject; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Documented @Retention(RUNTIME) @Target(TYPE) @Inherited public @interface InjectProperties { /** * The name prefix of the properties that are valid to bind to this object. * @return the name prefix of the properties to bind */ String prefix() default ""; } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.inject; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Documented @Retention(RUNTIME) @Target(FIELD) public @interface InjectProperty { /** * override prefix of {@link InjectProperties} * @return prefix of keys */ String prefix() default ""; /** *
   * The name of the property
   * 1.support priority
   *   high,middle,low
   * 2.support placeholder
   *   name is root.${placeholder-1}.name
   *   placeholder-1 is k1
   *   then final name is root.k1.name
   * 3.placeholder can be a list
   *   name is root.${placeholder-1}.name
   *   placeholder-1 is a list, value is k1-1/k1-2
   *   then final name is root.k1-1.name/root.k1-2.name
   * 
* @return the name of the property */ String[] keys() default {}; String defaultValue() default ""; } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/PlaceholderResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.inject; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; /** *
 * not care for performance
 *
 * behavior of multiple list if defined:
 *   org.apache.servicecomb.config.inject.TestPlaceholderResolver#multi_list()
 * behavior of nested and multiple list variable is undefined
 *   org.apache.servicecomb.config.inject.TestPlaceholderResolver#mixed()
 * 
*/ public class PlaceholderResolver { private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("(?\\\\)?\\$\\{(?[^{}]+)\\}"); static class SplitPart { boolean var; String fullName; Object value; public SplitPart(boolean var, String fullName) { this.var = var; this.fullName = fullName; } @Override public String toString() { return "SplitPart{" + "var=" + var + ", fullName='" + fullName + '\'' + ", value=" + value + '}'; } } static class Row { List parts = new ArrayList<>(); int cartesianProductCount = 1; int varCount = 0; } public String replaceFirst(String str) { return replace(str, Collections.emptyMap()).get(0); } public String replaceFirst(String str, Map parameters) { return replace(str, parameters).get(0); } public List replace(String str, Map parameters) { List finalRows = replaceToRows(str, parameters); List replaced = new ArrayList<>(); for (Row row : finalRows) { resolve(row, replaced); } replaced.replaceAll(s -> s.replace("\\$", "$")); return replaced; } private List replaceToRows(String str, Map parameters) { List finalRows = new ArrayList<>(); List remainRows = new ArrayList<>(); replaceToRows(str, parameters, remainRows, finalRows); for (String row : remainRows) { List nestedRows = replaceToRows(row, parameters); finalRows.addAll(nestedRows); } return finalRows; } private void replaceToRows(String str, Map parameters, List remainRows, List finalRows) { Row row = parseToRow(str, parameters); if (row.varCount == 0 && row.cartesianProductCount == 1) { finalRows.add(row); return; } resolve(row, remainRows); } private Row parseToRow(String str, Map parameters) { Matcher matcher = PLACEHOLDER_PATTERN.matcher(str); Row row = new Row(); int last = 0; while (matcher.find()) { row.parts.add(new SplitPart(false, str.substring(last, matcher.start()))); last = matcher.end(); if (matcher.group("escape") != null) { row.parts.add(new SplitPart(false, matcher.group().substring(1))); continue; } String name = matcher.group("name"); Object value = findValue(parameters, name); if (value instanceof Collection) { row.cartesianProductCount *= ((Collection) value).size(); } if (value != null) { row.varCount++; } SplitPart splitPart = new SplitPart(value != null, matcher.group()); splitPart.value = value; row.parts.add(splitPart); } row.parts.add(new SplitPart(false, str.substring(last))); return row; } // resolve placeholder and execute cartesian product @SuppressWarnings("unchecked") private void resolve(Row row, List resolvedRows) { List stringBuilders = new ArrayList<>(); for (int idx = 0; idx < row.cartesianProductCount; idx++) { stringBuilders.add(new StringBuilder()); } int collectionRepeatCount = 1; for (SplitPart part : row.parts) { if (!part.var) { for (int idx = 0; idx < row.cartesianProductCount; idx++) { StringBuilder sb = stringBuilders.get(idx); if (part.fullName.startsWith("$")) { sb.append("\\" + part.fullName); continue; } sb.append(part.fullName); } continue; } if (part.value instanceof Collection) { int size = ((Collection) part.value).size(); int rowRepeatCount = row.cartesianProductCount / size / collectionRepeatCount; int valueIdx = 0; for (int collectionRepeatIdx = 0; collectionRepeatIdx < collectionRepeatCount; collectionRepeatIdx++) { for (String value : (Collection) part.value) { for (int repeatIdx = 0; repeatIdx < rowRepeatCount; repeatIdx++) { StringBuilder sb = stringBuilders.get(valueIdx); valueIdx++; sb.append(value); } } } collectionRepeatCount *= size; continue; } // normal var for (int idx = 0; idx < row.cartesianProductCount; idx++) { StringBuilder sb = stringBuilders.get(idx); sb.append(part.value); } } for (StringBuilder sb : stringBuilders) { resolvedRows.add(sb.toString()); } } private Object findValue(Map parameters, String key) { Object value = parameters.get(key); if (value == null) { value = LegacyPropertyFactory.getStringProperty(key); } return value; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/Parser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.parser; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.commons.lang3.StringUtils; public interface Parser { String CONTENT_TYPE_YAML = "yaml"; String CONTENT_TYPE_PROPERTIES = "properties"; String CONTENT_TYPE_RAW = "raw"; YamlParser yamlParser = new YamlParser(); PropertiesParser propertiesParser = new PropertiesParser(); RawParser rawParser = new RawParser(); Map parse(String content, String prefix, boolean addPrefix); static Parser findParser(String contentType) { switch (contentType) { case CONTENT_TYPE_YAML: return yamlParser; case CONTENT_TYPE_PROPERTIES: return propertiesParser; case CONTENT_TYPE_RAW: return rawParser; default: throw new IllegalArgumentException("not supported contentType=" + contentType); } } @SuppressWarnings("unchecked") static Map propertiesToMap(Properties properties, String prefix, boolean addPrefix) { Map result = new HashMap<>(); Enumeration keys = (Enumeration) properties.propertyNames(); while (keys.hasMoreElements()) { String key = keys.nextElement(); String value = properties.getProperty(key); if (addPrefix && !StringUtils.isEmpty(prefix)) { key = prefix + "." + key; } if (value == null) { result.put(key, null); } else { result.put(key, value.trim()); } } return result; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/PropertiesParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.parser; import java.io.IOException; import java.io.StringReader; import java.util.Map; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PropertiesParser implements Parser { private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesParser.class); @Override public Map parse(String content, String prefix, boolean addPrefix) { Properties properties = new Properties(); try { properties.load(new StringReader(content)); } catch (IOException e) { LOGGER.error("parse properties content failed, message={}", e.getMessage()); } return Parser.propertiesToMap(properties, prefix, addPrefix); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/RawParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.parser; import java.util.HashMap; import java.util.Map; public class RawParser implements Parser { @Override public Map parse(String content, String prefix, boolean addPrefix) { Map result = new HashMap<>(); result.put(prefix, content); return result; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/YamlParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.parser; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Properties; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ByteArrayResource; public class YamlParser implements Parser { @Override public Map parse(String content, String prefix, boolean addPrefix) { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8))); Properties properties = yamlFactory.getObject(); return Parser.propertiesToMap(properties, prefix, addPrefix); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import java.util.List; public class ConfigObject { private final T instance; private final List properties; public ConfigObject(T instance, List properties) { this.instance = instance; this.properties = properties; } public T getInstance() { return instance; } public List getProperties() { return properties; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.apache.servicecomb.config.inject.PlaceholderResolver; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.type.TypeFactory; /** * must create by PriorityPropertyManager
* or register to PriorityPropertyManager manually
*
* ${} or ${not-exist-key} is valid key in archaius
* so this wrapper mechanism will not throw exception even can not find value by placeholder */ public class ConfigObjectFactory { private final PriorityPropertyFactory propertyFactory; private final Map, JavaType> classCache = new ConcurrentHashMapEx<>(); private final Map javaTypeCache = new ConcurrentHashMapEx<>(); private final Map> beanDescriptionCache = new ConcurrentHashMapEx<>(); public ConfigObjectFactory(PriorityPropertyFactory propertyFactory) { this.propertyFactory = propertyFactory; } public PriorityPropertyFactory getPropertyFactory() { return propertyFactory; } public ConfigObject create(Class cls, Map parameters) { try { return create(cls.getDeclaredConstructor().newInstance(), parameters); } catch (Throwable e) { throw new IllegalStateException("create config object failed, class=" + cls.getName(), e); } } public ConfigObject create(T instance, Map parameters) { String prefix = initPrefix(instance.getClass()); List properties = createProperties(instance, prefix, parameters); return new ConfigObject<>(instance, properties); } private String initPrefix(Class cls) { InjectProperties injectProperties = cls.getAnnotation(InjectProperties.class); if (injectProperties == null) { return ""; } String prefix = injectProperties.prefix(); if (prefix.isEmpty()) { return ""; } return prefix + "."; } public List createProperties(Object instance, String prefix, Map parameters) { List properties = new ArrayList<>(); JavaType javaType = classCache.computeIfAbsent(instance.getClass(), TypeFactory.defaultInstance()::constructType); BeanDescription beanDescription = javaTypeCache.computeIfAbsent(javaType, JsonUtils.OBJ_MAPPER.getSerializationConfig()::introspect); for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) { if (propertyDefinition.getField() == null) { continue; } if (propertyDefinition.getSetter() == null && !propertyDefinition.getField().isPublic()) { continue; } PriorityProperty priorityProperty = createPriorityProperty(propertyDefinition.getField().getAnnotated(), prefix, parameters); if (priorityProperty == null) { continue; } Setter setter = beanDescriptionCache.computeIfAbsent(propertyDefinition, LambdaMetafactoryUtils::createObjectSetter); setter.set(instance, priorityProperty.getValue()); properties.add(new ConfigObjectProperty(setter, priorityProperty)); } return properties; } private PriorityProperty createPriorityProperty(Field field, String prefix, Map parameters) { String[] keys = collectPropertyKeys(field, prefix, parameters); Class fieldCls = field.getType(); switch (fieldCls.getName()) { case "int": return createIntProperty(field, keys, 0); case "java.lang.Integer": return createIntProperty(field, keys, null); case "long": return createLongProperty(field, keys, 0L); case "java.lang.Long": return createLongProperty(field, keys, null); case "java.lang.String": return createStringProperty(field, keys); case "float": return createFloatProperty(field, keys, 0f); case "java.lang.Float": return createFloatProperty(field, keys, null); case "double": return createDoubleProperty(field, keys, 0.0); case "java.lang.Double": return createDoubleProperty(field, keys, null); case "boolean": return createBooleanProperty(field, keys, false); case "java.lang.Boolean": return createBooleanProperty(field, keys, null); default: return null; } } private PriorityProperty createStringProperty(Field field, String[] keys) { String defaultValue = null; InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.defaultValue().isEmpty()) { defaultValue = injectProperty.defaultValue(); } } return propertyFactory.getOrCreate(String.class, null, defaultValue, keys); } private PriorityProperty createDoubleProperty(Field field, String[] keys, Double defaultValue) { InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.defaultValue().isEmpty()) { defaultValue = Double.parseDouble(injectProperty.defaultValue()); } } return propertyFactory.getOrCreate(Double.class, null, defaultValue, keys); } private PriorityProperty createFloatProperty(Field field, String[] keys, Float defaultValue) { InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.defaultValue().isEmpty()) { defaultValue = Float.parseFloat(injectProperty.defaultValue()); } } return propertyFactory.getOrCreate(Float.class, null, defaultValue, keys); } private PriorityProperty createBooleanProperty(Field field, String[] keys, Boolean defaultValue) { InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.defaultValue().isEmpty()) { defaultValue = Boolean.parseBoolean(injectProperty.defaultValue()); } } return propertyFactory.getOrCreate(Boolean.class, null, defaultValue, keys); } private PriorityProperty createLongProperty(Field field, String[] keys, Long defaultValue) { InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.defaultValue().isEmpty()) { defaultValue = Long.parseLong(injectProperty.defaultValue()); } } return propertyFactory.getOrCreate(Long.class, null, defaultValue, keys); } private PriorityProperty createIntProperty(Field field, String[] keys, Integer defaultValue) { InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.defaultValue().isEmpty()) { defaultValue = Integer.parseInt(injectProperty.defaultValue()); } } return propertyFactory.getOrCreate(Integer.class, null, defaultValue, keys); } private String[] collectPropertyKeys(Field field, String prefix, Map parameters) { String propertyPrefix = prefix; String[] keys = new String[] {field.getName()}; InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); if (injectProperty != null) { if (!injectProperty.prefix().isEmpty()) { propertyPrefix = injectProperty.prefix() + "."; } if (injectProperty.keys().length != 0) { keys = injectProperty.keys(); } } List finalKeys = new ArrayList<>(); for (String key : keys) { List resolvedKeys = new PlaceholderResolver().replace(propertyPrefix + key, parameters); finalKeys.addAll(resolvedKeys); } return finalKeys.toArray(new String[0]); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectProperty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import org.apache.servicecomb.foundation.common.utils.bean.Setter; /** * do not reference config object instance, otherwise gc for weak hash map will failed */ public class ConfigObjectProperty { private final Setter setter; private final PriorityProperty property; public ConfigObjectProperty(Setter setter, PriorityProperty property) { this.setter = setter; this.property = property; } public void updateValueWhenChanged(Object instance, String changedKey) { if (property.isChangedKey(changedKey)) { property.updateValue(); setter.set(instance, property.getValue()); } } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/DynamicProperty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import org.springframework.core.env.Environment; public class DynamicProperty { private final Environment environment; private final String propName; public DynamicProperty(Environment environment, String propName) { this.environment = environment; this.propName = propName; } public Integer getInteger() { return environment.getProperty(propName, Integer.class); } public Long getLong() { return environment.getProperty(propName, Long.class); } public String getString() { return environment.getProperty(propName); } public Boolean getBoolean() { return environment.getProperty(propName, Boolean.class); } public Double getDouble() { return environment.getProperty(propName, Double.class); } public Float getFloat() { return environment.getProperty(propName, Float.class); } public String getName() { return propName; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Objects; import java.util.function.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; /** * must create by PriorityPropertyManager
* or register to PriorityPropertyManager manually * @param */ public final class PriorityProperty { private static final Logger LOGGER = LoggerFactory.getLogger(PriorityProperty.class); private final PriorityPropertyType propertyType; private final String joinedPriorityKeys; private final Function internalValueReader; private final DynamicProperty[] properties; private T finalValue; public PriorityProperty(Environment environment, PriorityPropertyType propertyType) { this.propertyType = propertyType; this.joinedPriorityKeys = Arrays.toString(propertyType.getPriorityKeys()); this.internalValueReader = collectReader(propertyType.getType()); this.properties = createProperties(environment, propertyType.getPriorityKeys()); initValue(); } private DynamicProperty[] createProperties(Environment environment, String[] priorityKeys) { DynamicProperty[] properties = new DynamicProperty[priorityKeys.length]; for (int idx = 0; idx < priorityKeys.length; idx++) { String key = priorityKeys[idx].trim(); properties[idx] = new DynamicProperty(environment, key); } return properties; } private Function collectReader(Type type) { if (type == int.class || type == Integer.class) { return this::readInt; } if (type == long.class || type == Long.class) { return this::readLong; } if (type == String.class) { return this::readString; } if (type == boolean.class || type == Boolean.class) { return this::readBoolean; } if (type == double.class || type == Double.class) { return this::readDouble; } if (type == float.class || type == Float.class) { return this::readFloat; } throw new IllegalStateException("not support, type=" + type.getTypeName()); } @SuppressWarnings("unchecked") private T readInt(DynamicProperty property) { return (T) property.getInteger(); } @SuppressWarnings("unchecked") private T readLong(DynamicProperty property) { return (T) property.getLong(); } @SuppressWarnings("unchecked") private T readString(DynamicProperty property) { return (T) property.getString(); } @SuppressWarnings("unchecked") private T readBoolean(DynamicProperty property) { return (T) property.getBoolean(); } @SuppressWarnings("unchecked") private T readDouble(DynamicProperty property) { return (T) property.getDouble(); } @SuppressWarnings("unchecked") private T readFloat(DynamicProperty property) { return (T) property.getFloat(); } public String[] getPriorityKeys() { return propertyType.getPriorityKeys(); } public T getDefaultValue() { return propertyType.getDefaultValue(); } public DynamicProperty[] getProperties() { return properties; } void initValue() { String effectiveKey = doUpdateFinalValue(); LOGGER.debug("config inited, \"{}\" set to {}, effective key is \"{}\".", joinedPriorityKeys, finalValue, effectiveKey); } synchronized boolean updateValue() { T lastValue = finalValue; String effectiveKey = doUpdateFinalValue(); if (effectiveKey != null) { LOGGER.debug("config changed, \"{}\" changed from {} to {}, effective key is \"{}\".", joinedPriorityKeys, lastValue, finalValue, effectiveKey); return true; } return false; } /** * * @return if value changed, return effectiveKey, otherwise null */ private String doUpdateFinalValue() { T lastValue = finalValue; String effectiveKey = "default value"; T value = propertyType.getDefaultValue(); for (DynamicProperty property : properties) { T propValue = internalValueReader.apply(property); if (propValue == null || propValue.equals(propertyType.getInvalidValue())) { continue; } effectiveKey = property.getName(); value = propValue; break; } if (Objects.equals(lastValue, value)) { return null; } finalValue = value; return effectiveKey; } public T getValue() { return finalValue; } public boolean isChangedKey(String changedKey) { if (changedKey == null) { // property source changed or clear, and so on return true; } for (DynamicProperty property : properties) { if (changedKey.equals(property.getName())) { return true; } } return false; } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import java.lang.reflect.Type; import java.util.Map; import java.util.stream.Stream; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; public class PriorityPropertyFactory { private final Map, PriorityProperty> properties = new ConcurrentHashMapEx<>(); private final Environment environment; public PriorityPropertyFactory(Environment environment) { this.environment = environment; } @VisibleForTesting public Stream> getProperties() { return properties.values().stream(); } @SuppressWarnings("unchecked") public PriorityProperty getOrCreate(Type type, T invalidValue, T defaultValue, String... priorityKeys) { PriorityPropertyType propertyType = new PriorityPropertyType<>(type, invalidValue, defaultValue, priorityKeys); return (PriorityProperty) properties.computeIfAbsent(propertyType, key -> new PriorityProperty<>(environment, key)); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import static java.util.Collections.synchronizedMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.WeakHashMap; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.foundation.common.event.EventManager; import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; public class PriorityPropertyManager { private final ConfigObjectFactory configObjectFactory; // key is config object instance // value is properties of the config object instance private final Map> configObjectMap = synchronizedMap(new WeakHashMap<>()); public PriorityPropertyManager(ConfigObjectFactory configObjectFactory) { this.configObjectFactory = configObjectFactory; EventManager.register(this); } public PriorityPropertyFactory getPropertyFactory() { return configObjectFactory.getPropertyFactory(); } public void close() { } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { // just loop all properties, it's very fast, no need to do any optimize for (Entry> entry : configObjectMap.entrySet()) { Object instance = entry.getKey(); entry.getValue().forEach( configObjectProperty -> { for (String updatedKey : event.getChanged()) { configObjectProperty.updateValueWhenChanged(instance, updatedKey); } }); } } @VisibleForTesting public Map> getConfigObjectMap() { return configObjectMap; } public T createConfigObject(Class cls, Object... kvs) { Map parameters = new HashMap<>(); for (int idx = 0; idx < kvs.length; idx += 2) { parameters.put(kvs[idx].toString(), kvs[idx + 1]); } return createConfigObject(cls, parameters); } public T createConfigObject(Class cls, Map parameters) { ConfigObject configObject = configObjectFactory.create(cls, parameters); return saveConfigObject(configObject); } public T createConfigObject(T instance, Map parameters) { ConfigObject configObject = configObjectFactory.create(instance, parameters); return saveConfigObject(configObject); } private T saveConfigObject(ConfigObject configObject) { configObjectMap.put(configObject.getInstance(), configObject.getProperties()); return configObject.getInstance(); } } ================================================ FILE: foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Objects; public class PriorityPropertyType { private final Type type; // when got invalid value will try next level // null always be a invalid value private final T invalidValue; // when got invalid value by lowest level, will use defaultValue private final T defaultValue; // priorityKeys[0] has the highest priority private final String[] priorityKeys; public PriorityPropertyType(Type type, T invalidValue, T defaultValue, String... priorityKeys) { this.type = type; this.invalidValue = invalidValue; this.defaultValue = defaultValue; this.priorityKeys = priorityKeys; } public Type getType() { return type; } public T getInvalidValue() { return invalidValue; } public T getDefaultValue() { return defaultValue; } public String[] getPriorityKeys() { return priorityKeys; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PriorityPropertyType that = (PriorityPropertyType) o; return type.equals(that.type) && Objects.equals(invalidValue, that.invalidValue) && Objects .equals(defaultValue, that.defaultValue) && Arrays.equals(priorityKeys, that.priorityKeys); } @Override public int hashCode() { int result = Objects.hash(type, invalidValue, defaultValue); result = 31 * result + Arrays.hashCode(priorityKeys); return result; } } ================================================ FILE: foundations/foundation-config/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.config.InMemoryDynamicPropertiesSource ================================================ FILE: foundations/foundation-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.config.FoundationConfigConfiguration ================================================ FILE: foundations/foundation-config/src/main/resources/META-INF/spring.factories ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.springframework.boot.env.EnvironmentPostProcessor=\ org.apache.servicecomb.config.ConfigEnvironmentPostProcessor ================================================ FILE: foundations/foundation-config/src/main/resources/mapping.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: -100 SERVICECOMB_ENV: service_description.environment servicecomb.loadbalance.stats.timerIntervalInMilis: - servicecomb.loadbalance.stats.timerIntervalInMillis ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import static org.apache.servicecomb.foundation.test.scaffolding.AssertUtils.assertPrettyJson; import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; import java.util.Map.Entry; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; public class BootStrapPropertiesTest { private Map readInstanceProperties(String yaml) { Map properties = YAMLUtil.yaml2Properties(yaml); ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); MapPropertySource propertySource = new MapPropertySource("yaml", properties); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); for (Entry entry : properties.entrySet()) { Mockito.when(environment.getProperty(entry.getKey())).thenReturn(entry.getValue().toString()); } return BootStrapProperties.readServiceInstanceProperties(environment); } @Test void should_be_empty_when_old_and_new_key_not_exists() { Map properties = readInstanceProperties("k: v"); assertThat(properties).isEmpty(); } @Test void should_read_boolean_to_string() { Map properties = readInstanceProperties(""" servicecomb: instance: properties: k: true"""); assertPrettyJson(properties).isEqualTo(""" { "k" : "true" }"""); } @Test void should_read_number_to_string() { Map properties = readInstanceProperties(""" servicecomb: instance: properties: k: 1"""); assertPrettyJson(properties).isEqualTo(""" { "k" : "1" }"""); } @Test void should_read_by_old_prefix_when_new_prefix_not_exists() { Map properties = readInstanceProperties(""" instance_description: properties: k: v k1: v1"""); assertPrettyJson(properties).isEqualTo(""" { "k1" : "v1", "k" : "v" }"""); } @Test void should_ignore_old_prefix_when_new_prefix_exists() { Map properties = readInstanceProperties(""" instance_description: properties: k: v k1: v1 servicecomb: instance: properties: k: new"""); assertPrettyJson(properties).isEqualTo(""" { "k" : "new" }"""); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/DynamicPropertiesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import static com.seanyinx.github.unit.scaffolding.Randomness.nextBoolean; import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.number.IsCloseTo.closeTo; import java.util.HashMap; import java.util.Objects; import org.apache.servicecomb.foundation.common.event.EventManager; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.seanyinx.github.unit.scaffolding.Poller; import com.seanyinx.github.unit.scaffolding.Randomness; public class DynamicPropertiesTest { private static final String stringPropertyName = uniquify("stringPropertyName"); private static final String intPropertyName = uniquify("intPropertyName"); private static final String longPropertyName = uniquify("longPropertyName"); private static final String floatPropertyName = uniquify("floatPropertyName"); private static final String doublePropertyName = uniquify("doublePropertyName"); private static final String booleanPropertyName = uniquify("booleanPropertyName"); private static final String stringOldValue = uniquify("stringPropertyValue"); private static final int intOldValue = Randomness.nextInt(); private static final long longOldValue = Randomness.nextLong(); private static final float floatOldValue = Double.valueOf(Randomness.nextDouble()).floatValue(); private static final double doubleOldValue = Randomness.nextDouble(); private static final boolean booleanOldValue = nextBoolean(); private static final double ERROR = 0.0000001; private static Environment environment; private static DynamicProperties dynamicProperties; private final Poller poller = new Poller(2000, 100); private volatile String stringPropertyValue; private volatile int intPropertyValue; private volatile long longPropertyValue; private volatile double floatPropertyValue; private volatile double doublePropertyValue; private volatile boolean booleanPropertyValue; @BeforeClass public static void setUpClass() throws Exception { environment = Mockito.mock(Environment.class); writeInitialConfig(); dynamicProperties = new DynamicPropertiesImpl(environment); } @AfterClass public static void tearDown() throws Exception { } private static void writeInitialConfig() throws Exception { Mockito.when(environment.getProperty(stringPropertyName, (String) null)).thenReturn(stringOldValue); Mockito.when(environment.getProperty(intPropertyName, int.class, 0)).thenReturn(intOldValue); Mockito.when(environment.getProperty(longPropertyName, long.class, 0L)).thenReturn(longOldValue); Mockito.when(environment.getProperty(floatPropertyName, float.class, 0F)).thenReturn(floatOldValue); Mockito.when(environment.getProperty(doublePropertyName, double.class, 0D)).thenReturn(doubleOldValue); Mockito.when(environment.getProperty(booleanPropertyName, boolean.class, false)) .thenReturn(booleanOldValue); } @Test public void observesSpecifiedStringProperty() throws Exception { String property = dynamicProperties.getStringProperty(stringPropertyName, null); assertThat(property, is(stringOldValue)); property = dynamicProperties.getStringProperty(stringPropertyName, value -> stringPropertyValue = value, null); assertThat(property, is(stringOldValue)); String newValue = uniquify("newValue"); Mockito.when(environment.getProperty(stringPropertyName, (String) null)).thenReturn(newValue); HashMap updated = new HashMap<>(); updated.put(stringPropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> Objects.equals(stringPropertyValue, newValue)); } @Test public void observesSpecifiedIntProperty() throws Exception { int property = dynamicProperties.getIntProperty(intPropertyName, 0); assertThat(property, is(intOldValue)); property = dynamicProperties.getIntProperty(intPropertyName, value -> intPropertyValue = value, 0); assertThat(property, is(intOldValue)); int newValue = Randomness.nextInt(); Mockito.when(environment.getProperty(intPropertyName, Integer.class, 0)).thenReturn(newValue); HashMap updated = new HashMap<>(); updated.put(intPropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> intPropertyValue == newValue); } @Test public void observesSpecifiedLongProperty() throws Exception { Mockito.when(environment.getProperty(longPropertyName, long.class, 3L)).thenReturn(longOldValue); long property = dynamicProperties.getLongProperty(longPropertyName, 0); assertThat(property, is(longOldValue)); property = dynamicProperties.getLongProperty(longPropertyName, value -> longPropertyValue = value, 3L); assertThat(property, is(longOldValue)); long newValue = Randomness.nextLong(); Mockito.when(environment.getProperty(longPropertyName, Long.class, 3L)).thenReturn(newValue); HashMap updated = new HashMap<>(); updated.put(longPropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Mockito.when(environment.getProperty(longPropertyName, Long.class, 3L)).thenReturn(3L); HashMap deleted = new HashMap<>(); deleted.put(longPropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(new HashMap<>(), new HashMap<>(), deleted)); poller.assertEventually(() -> longPropertyValue == 3L); } @Test public void observesSpecifiedFloatProperty() throws Exception { double property = dynamicProperties.getFloatProperty(floatPropertyName, 0); assertThat(property, closeTo(floatOldValue, ERROR)); property = dynamicProperties.getFloatProperty(floatPropertyName, value -> floatPropertyValue = value, 0f); assertThat(property, closeTo(floatOldValue, ERROR)); float newValue = Double.valueOf(Randomness.nextDouble()).floatValue(); Mockito.when(environment.getProperty(floatPropertyName, Float.class, 0f)).thenReturn(newValue); HashMap updated = new HashMap<>(); updated.put(floatPropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> Math.abs(floatPropertyValue - newValue) < ERROR); } @Test public void observesSpecifiedDoubleProperty() throws Exception { double property = dynamicProperties.getDoubleProperty(doublePropertyName, 0); assertThat(property, closeTo(doubleOldValue, ERROR)); property = dynamicProperties.getDoubleProperty(doublePropertyName, value -> doublePropertyValue = value, 0d); assertThat(property, closeTo(doubleOldValue, ERROR)); double newValue = Randomness.nextDouble(); Mockito.when(environment.getProperty(doublePropertyName, Double.class, 0d)).thenReturn(newValue); HashMap updated = new HashMap<>(); updated.put(doublePropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> Math.abs(doublePropertyValue - newValue) < ERROR); } @Test public void observesSpecifiedBooleanProperty() throws Exception { boolean property = dynamicProperties.getBooleanProperty(booleanPropertyName, false); assertThat(property, is(booleanOldValue)); property = dynamicProperties.getBooleanProperty( booleanPropertyName, value -> booleanPropertyValue = value, false); assertThat(property, is(booleanOldValue)); boolean newValue = !booleanOldValue; Mockito.when(environment.getProperty(booleanPropertyName, Boolean.class, booleanOldValue)).thenReturn(newValue); HashMap updated = new HashMap<>(); updated.put(booleanPropertyName, newValue); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); poller.assertEventually(() -> booleanPropertyValue == newValue); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigMapping.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestConfigMapping { @Test public void testMapping() { List value = ConfigMapping.map("SERVICECOMB_ENV"); Assertions.assertEquals(2, value.size()); Assertions.assertEquals("service_description.environment", value.get(0)); Assertions.assertEquals("service_description.environment.old", value.get(1)); Map m = ConfigMapping.getMapping(); Assertions.assertNotNull(m); } @Test public void testConvertedMap() { String value = ConfigMapping.map("CSE_ENV_MAPPING"); Map m = ConfigMapping.getMapping(); Map m1 = ConfigMapping.getConvertedMap(m); Assertions.assertEquals("servicecomb.testmapping.key", value); Assertions.assertNotNull(m1); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestYAMLUtil { public static class Person { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public static class UnsafePerson { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } @Test public void testSafeParser() { Person person = YAMLUtil.parserObject("name: hello", Person.class); Assertions.assertEquals("hello", person.getName()); } @Test public void testYamlConfig() { RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class, () -> YAMLUtil.yaml2Properties("servicecomb.service.registry.enabled: {{true}}")); Assertions.assertEquals("input cannot be convert to map", runtimeException.getMessage()); } @Test @SuppressWarnings("unchecked") public void testListValue() { Map result = YAMLUtil.yaml2Properties("hello: a,b"); Assertions.assertEquals(result.size(), 1); String listValue = (String) result.get("hello"); Assertions.assertEquals(listValue, "a,b"); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.file; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMicroserviceConfigLoader { private final MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); private ConfigModel createConfigModel(String protocol, int order, String file) throws MalformedURLException { ConfigModel configModel = new ConfigModel(); configModel.setUrl(new URL(protocol, null, file)); configModel.setOrder(order); return configModel; } @Test public void configsSortedByInsertionOrder() throws MalformedURLException { loader.getConfigModels().add(createConfigModel("jar", 0, "c")); loader.getConfigModels().add(createConfigModel("jar", 0, "b")); loader.getConfigModels().add(createConfigModel("jar", 0, "a")); loader.sort(); Assertions.assertEquals(urls("jar:c", "jar:b", "jar:a"), urlsOf(loader.getConfigModels())); } @Test public void configsSortedBySpecifiedOrder() throws MalformedURLException { loader.getConfigModels().add(createConfigModel("jar", 1, "b")); loader.getConfigModels().add(createConfigModel("jar", -10, "c")); loader.getConfigModels().add(createConfigModel("jar", Integer.MAX_VALUE, "a")); loader.sort(); Assertions.assertEquals(urls("jar:c", "jar:b", "jar:a"), urlsOf(loader.getConfigModels())); } @Test public void jarsAlwaysHaveHigherPriorityThanFiles() throws MalformedURLException { loader.getConfigModels().add(createConfigModel("file", 0, "f2")); loader.getConfigModels().add(createConfigModel("jar", 1, "j1")); loader.getConfigModels().add(createConfigModel("file", 0, "f1")); loader.sort(); Assertions.assertEquals(urls("jar:j1", "file:f2", "file:f1"), urlsOf(loader.getConfigModels())); } private String urlsOf(List configModels) { return String.join(",", configModels .stream() .map(configModel -> configModel.getUrl().toString()) .collect(Collectors.toList())); } private String urls(String... urls) { return String.join(",", (CharSequence[]) urls); } @Test public void testLoadEmptyYaml() throws IOException { loader.load("empty.yaml"); Assertions.assertTrue(loader.getConfigModels().get(0).getConfig().isEmpty()); } @Test public void testLoadNotExistYaml() throws IOException { URL url = URI.create("file:/notExist.yaml").toURL(); try { loader.load(url); Assertions.fail("must throw exception"); } catch (FileNotFoundException e) { Assertions.assertTrue(true); } } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestConfigObjectFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.inject; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.priority.TestPriorityPropertyBase; import org.apache.servicecomb.foundation.common.event.EventManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestConfigObjectFactory extends TestPriorityPropertyBase { public static class ConfigNoAnnotation { public String strValue; private String strValue1; public int intValue; private int intValue1; public Integer intValueObj; private Integer intValueObj1; public long longValue; private long longValue1; public Long longValueObj; private Long longValueObj1; public float floatValue; private float floatValue1; public Float floatValueObj; private Float floatValueObj1; public double doubleValue; private double doubleValue1; public Double doubleValueObj; private Double doubleValueObj1; public boolean booleanValue; private boolean booleanValue1; public Boolean booleanValueObj; private Boolean booleanValueObj1; public String getStrValue1() { return strValue1; } public void setStrValue1(String strValue1) { this.strValue1 = strValue1; } public int getIntValue1() { return intValue1; } public void setIntValue1(int intValue1) { this.intValue1 = intValue1; } public Integer getIntValueObj1() { return intValueObj1; } public void setIntValueObj1(Integer intValueObj1) { this.intValueObj1 = intValueObj1; } public long getLongValue1() { return longValue1; } public void setLongValue1(long longValue1) { this.longValue1 = longValue1; } public Long getLongValueObj1() { return longValueObj1; } public void setLongValueObj1(Long longValueObj1) { this.longValueObj1 = longValueObj1; } public float getFloatValue1() { return floatValue1; } public void setFloatValue1(float floatValue1) { this.floatValue1 = floatValue1; } public Float getFloatValueObj1() { return floatValueObj1; } public void setFloatValueObj1(Float floatValueObj1) { this.floatValueObj1 = floatValueObj1; } public double getDoubleValue1() { return doubleValue1; } public void setDoubleValue1(double doubleValue1) { this.doubleValue1 = doubleValue1; } public Double getDoubleValueObj1() { return doubleValueObj1; } public void setDoubleValueObj1(Double doubleValueObj1) { this.doubleValueObj1 = doubleValueObj1; } public boolean isBooleanValue1() { return booleanValue1; } public void setBooleanValue1(boolean booleanValue1) { this.booleanValue1 = booleanValue1; } public Boolean getBooleanValueObj1() { return booleanValueObj1; } public void setBooleanValueObj1(Boolean booleanValueObj1) { this.booleanValueObj1 = booleanValueObj1; } } @Test public void noAnnotation_defaultValue() { ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); Assertions.assertNull(config.strValue); Assertions.assertNull(config.getStrValue1()); Assertions.assertEquals(0, config.intValue); Assertions.assertEquals(0, config.getIntValue1()); Assertions.assertNull(config.intValueObj); Assertions.assertNull(config.getIntValueObj1()); Assertions.assertEquals(0, config.longValue); Assertions.assertEquals(0, config.getLongValue1()); Assertions.assertNull(config.longValueObj); Assertions.assertNull(config.getLongValueObj1()); Assertions.assertEquals(0, config.floatValue, 0); Assertions.assertEquals(0, config.getFloatValue1(), 0); Assertions.assertNull(config.floatValueObj); Assertions.assertNull(config.getFloatValueObj1()); Assertions.assertEquals(0, config.doubleValue, 0); Assertions.assertEquals(0, config.getDoubleValue1(), 0); Assertions.assertNull(config.doubleValueObj); Assertions.assertNull(config.getDoubleValueObj1()); Assertions.assertFalse(config.booleanValue); Assertions.assertFalse(config.isBooleanValue1()); Assertions.assertNull(config.booleanValueObj); Assertions.assertNull(config.getBooleanValueObj1()); } @Test public void noAnnotation_initValue() { Mockito.when(environment.getProperty("strValue")).thenReturn("strValue"); Mockito.when(environment.getProperty("strValue1")).thenReturn("strValue1"); Mockito.when(environment.getProperty("intValue", Integer.class)).thenReturn(1); Mockito.when(environment.getProperty("intValue1", Integer.class)).thenReturn(2); Mockito.when(environment.getProperty("intValueObj", Integer.class)).thenReturn(3); Mockito.when(environment.getProperty("intValueObj1", Integer.class)).thenReturn(4); Mockito.when(environment.getProperty("longValue", Long.class)).thenReturn(5L); Mockito.when(environment.getProperty("longValue1", Long.class)).thenReturn(6L); Mockito.when(environment.getProperty("longValueObj", Long.class)).thenReturn(7L); Mockito.when(environment.getProperty("longValueObj1", Long.class)).thenReturn(8L); Mockito.when(environment.getProperty("floatValue", Float.class)).thenReturn(9.0F); Mockito.when(environment.getProperty("floatValue1", Float.class)).thenReturn(10.0F); Mockito.when(environment.getProperty("floatValueObj", Float.class)).thenReturn(11.0F); Mockito.when(environment.getProperty("floatValueObj1", Float.class)).thenReturn(12.0F); Mockito.when(environment.getProperty("doubleValue", Double.class)).thenReturn(13.0D); Mockito.when(environment.getProperty("doubleValue1", Double.class)).thenReturn(14.0D); Mockito.when(environment.getProperty("doubleValueObj", Double.class)).thenReturn(15.0D); Mockito.when(environment.getProperty("doubleValueObj1", Double.class)).thenReturn(16.0D); Mockito.when(environment.getProperty("booleanValue", Boolean.class)).thenReturn(true); Mockito.when(environment.getProperty("booleanValue1", Boolean.class)).thenReturn(true); Mockito.when(environment.getProperty("booleanValueObj", Boolean.class)).thenReturn(true); Mockito.when(environment.getProperty("booleanValueObj1", Boolean.class)).thenReturn(true); ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); Assertions.assertEquals("strValue", config.strValue); Assertions.assertEquals("strValue1", config.getStrValue1()); Assertions.assertEquals(1, config.intValue); Assertions.assertEquals(2, config.getIntValue1()); Assertions.assertEquals(3, (int) config.intValueObj); Assertions.assertEquals(4, (int) config.getIntValueObj1()); Assertions.assertEquals(5, config.longValue); Assertions.assertEquals(6, config.getLongValue1()); Assertions.assertEquals(7, (long) config.longValueObj); Assertions.assertEquals(8, (long) config.getLongValueObj1()); Assertions.assertEquals(9, config.floatValue, 0); Assertions.assertEquals(10, config.getFloatValue1(), 0); Assertions.assertEquals(11, config.floatValueObj, 0); Assertions.assertEquals(12, config.getFloatValueObj1(), 0); Assertions.assertEquals(13, config.doubleValue, 0); Assertions.assertEquals(14, config.getDoubleValue1(), 0); Assertions.assertEquals(15, config.doubleValueObj, 0); Assertions.assertEquals(16, config.getDoubleValueObj1(), 0); Assertions.assertTrue(config.booleanValue); Assertions.assertTrue(config.isBooleanValue1()); Assertions.assertTrue(config.booleanValueObj); Assertions.assertTrue(config.getBooleanValueObj1()); } @Test public void noAnnotation_updateValue() { ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); Mockito.when(environment.getProperty("strValue")).thenReturn("strValue"); Mockito.when(environment.getProperty("strValue1")).thenReturn("strValue1"); Mockito.when(environment.getProperty("intValue", Integer.class)).thenReturn(1); Mockito.when(environment.getProperty("intValue1", Integer.class)).thenReturn(2); Mockito.when(environment.getProperty("intValueObj", Integer.class)).thenReturn(3); Mockito.when(environment.getProperty("intValueObj1", Integer.class)).thenReturn(4); Mockito.when(environment.getProperty("longValue", Long.class)).thenReturn(5L); Mockito.when(environment.getProperty("longValue1", Long.class)).thenReturn(6L); Mockito.when(environment.getProperty("longValueObj", Long.class)).thenReturn(7L); Mockito.when(environment.getProperty("longValueObj1", Long.class)).thenReturn(8L); Mockito.when(environment.getProperty("floatValue", Float.class)).thenReturn(9.0F); Mockito.when(environment.getProperty("floatValue1", Float.class)).thenReturn(10.0F); Mockito.when(environment.getProperty("floatValueObj", Float.class)).thenReturn(11.0F); Mockito.when(environment.getProperty("floatValueObj1", Float.class)).thenReturn(12.0F); Mockito.when(environment.getProperty("doubleValue", Double.class)).thenReturn(13.0D); Mockito.when(environment.getProperty("doubleValue1", Double.class)).thenReturn(14.0D); Mockito.when(environment.getProperty("doubleValueObj", Double.class)).thenReturn(15.0D); Mockito.when(environment.getProperty("doubleValueObj1", Double.class)).thenReturn(16.0D); Mockito.when(environment.getProperty("booleanValue", Boolean.class)).thenReturn(true); Mockito.when(environment.getProperty("booleanValue1", Boolean.class)).thenReturn(true); Mockito.when(environment.getProperty("booleanValueObj", Boolean.class)).thenReturn(true); Mockito.when(environment.getProperty("booleanValueObj1", Boolean.class)).thenReturn(true); HashMap updated = new HashMap<>(); updated.put("strValue", "strValue"); updated.put("strValue1", "strValue1"); updated.put("intValue", 0); updated.put("intValue1", 1); updated.put("intValueObj", 2); updated.put("intValueObj1", 3); updated.put("longValue", 5L); updated.put("longValue1", 6L); updated.put("longValueObj", 7L); updated.put("longValueObj1", 8L); updated.put("floatValue", 9.0F); updated.put("floatValue1", 10.0F); updated.put("floatValueObj", 11.0F); updated.put("floatValueObj1", 12.0F); updated.put("doubleValue", 13.0D); updated.put("doubleValue1", 14.0D); updated.put("doubleValueObj", 15.0D); updated.put("doubleValueObj1", 16.0D); updated.put("booleanValue", true); updated.put("booleanValue1", true); updated.put("booleanValueObj", true); updated.put("booleanValueObj1", true); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals("strValue", config.strValue); Assertions.assertEquals("strValue1", config.getStrValue1()); Assertions.assertEquals(1, config.intValue); Assertions.assertEquals(2, config.getIntValue1()); Assertions.assertEquals(3, (int) config.intValueObj); Assertions.assertEquals(4, (int) config.getIntValueObj1()); Assertions.assertEquals(5, config.longValue); Assertions.assertEquals(6, config.getLongValue1()); Assertions.assertEquals(7, (long) config.longValueObj); Assertions.assertEquals(8, (long) config.getLongValueObj1()); Assertions.assertEquals(9, config.floatValue, 0); Assertions.assertEquals(10, config.getFloatValue1(), 0); Assertions.assertEquals(11, config.floatValueObj, 0); Assertions.assertEquals(12, config.getFloatValueObj1(), 0); Assertions.assertEquals(13, config.doubleValue, 0); Assertions.assertEquals(14, config.getDoubleValue1(), 0); Assertions.assertEquals(15, config.doubleValueObj, 0); Assertions.assertEquals(16, config.getDoubleValueObj1(), 0); Assertions.assertTrue(config.booleanValue); Assertions.assertTrue(config.isBooleanValue1()); Assertions.assertTrue(config.booleanValueObj); Assertions.assertTrue(config.getBooleanValueObj1()); } @InjectProperties(prefix = "root") public static class ConfigWithAnnotation { @InjectProperty(prefix = "override", keys = {"high", "low"}) public String strValue; @InjectProperty(keys = "${key}.value") public int intValue; @InjectProperty(keys = "${low-list}.a.${high-list}.b") public long longValue; @InjectProperty(keys = "${full-list}") public float floatValue; @InjectProperty(defaultValue = "abc") public String strDef; @InjectProperty(defaultValue = "1") public int intDef; @InjectProperty(defaultValue = "2") public long longDef; @InjectProperty(defaultValue = "3") public float floatDef; @InjectProperty(defaultValue = "4") public double doubleDef; @InjectProperty(defaultValue = "true") public boolean booleanDef; } @Test public void annotationDefault() { ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class); Assertions.assertEquals("abc", config.strDef); Assertions.assertEquals(1, config.intDef); Assertions.assertEquals(2, config.longDef); Assertions.assertEquals(3, config.floatDef, 0); Assertions.assertEquals(4, config.doubleDef, 0); Assertions.assertTrue(config.booleanDef); } @Test public void placeholder_multi_list() { ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class, "low-list", Arrays.asList("low-1", "low-2"), "high-list", Arrays.asList("high-1", "high-2")); // low-1.a.high-1.b // low-1.a.high-2.b // low-2.a.high-1.b // low-2.a.high-2.b Assertions.assertEquals(0, config.longValue); Mockito.when(environment.getProperty("root.low-2.a.high-2.b", Long.class)).thenReturn(Long.MAX_VALUE); HashMap updated = new HashMap<>(); updated.put("root.low-2.a.high-2.b", Long.MAX_VALUE); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE, config.longValue); Mockito.when(environment.getProperty("root.low-2.a.high-1.b", Long.class)).thenReturn(Long.MAX_VALUE - 1); updated = new HashMap<>(); updated.put("root.low-2.a.high-1.b", Long.MAX_VALUE - 1); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE - 1, config.longValue); Mockito.when(environment.getProperty("root.low-1.a.high-2.b", Long.class)).thenReturn(Long.MAX_VALUE - 2); updated = new HashMap<>(); updated.put("root.low-1.a.high-2.b", Long.MAX_VALUE - 2); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE - 2, config.longValue); Mockito.when(environment.getProperty("root.low-1.a.high-1.b", Long.class)).thenReturn(Long.MAX_VALUE - 3); updated = new HashMap<>(); updated.put("root.low-1.a.high-1.b", Long.MAX_VALUE - 3); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(Long.MAX_VALUE - 3, config.longValue); } @Test public void placeholder_full_list() { ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class, "full-list", Arrays.asList("l1-1", "l1-2")); Assertions.assertEquals(0, config.floatValue, 0); Mockito.when(environment.getProperty("root.l1-2", Float.class)).thenReturn(1F); HashMap updated = new HashMap<>(); updated.put("root.l1-2", 1F); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(1F, config.floatValue, 0); Mockito.when(environment.getProperty("root.l1-1", Float.class)).thenReturn(2F); updated = new HashMap<>(); updated.put("root.l1-1", 2F); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(2F, config.floatValue, 0); } @Test public void placeholder_normal() { ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class, "key", "k"); Assertions.assertEquals(0, config.intValue); Mockito.when(environment.getProperty("root.k.value", Integer.class)).thenReturn(1); Map updated = new HashMap<>(); updated.put("root.k.value", 1); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(1, config.intValue); } @Test public void overridePrefix() { ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class); Mockito.when(environment.getProperty("override.high")).thenReturn("high"); HashMap updated = new HashMap<>(); updated.put("override.high", "high"); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals("high", config.strValue); Mockito.when(environment.getProperty("override.high")).thenReturn(null); updated = new HashMap<>(); updated.put("override.high", null); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertNull(config.strValue); Mockito.when(environment.getProperty("override.low")).thenReturn("low"); updated = new HashMap<>(); updated.put("override.low", "low"); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals("low", config.strValue); Mockito.when(environment.getProperty("override.low")).thenReturn(null); updated = new HashMap<>(); updated.put("override.low", null); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertNull(config.strValue); } @InjectProperties(prefix = "root") public static class ConfigWithPojo { @InjectProperties(prefix = "root.model") public static class Model { @InjectProperty(defaultValue = "h") public String name; } @InjectProperty(defaultValue = "4") public int index; public Model model; } @Test public void configWithPojoWork() { ConfigWithPojo config = priorityPropertyManager.createConfigObject(ConfigWithPojo.class); ConfigWithPojo.Model model = priorityPropertyManager.createConfigObject(ConfigWithPojo.Model.class); config.model = model; Assertions.assertEquals(4, config.index); Assertions.assertEquals("h", config.model.name); Mockito.when(environment.getProperty("root.index", Integer.class)).thenReturn(5); Mockito.when(environment.getProperty("root.model.name")).thenReturn("w"); Map updated = new HashMap<>(); updated.put("root.index", 5); updated.put("root.model.name", "w"); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); Assertions.assertEquals(5, config.index); Assertions.assertEquals("w", config.model.name); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.inject; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestPlaceholderResolver { static Map parameters = new HashMap<>(); static PlaceholderResolver resolver = new PlaceholderResolver(); @BeforeAll public static void setupClass() { parameters.put("key", "value"); parameters.put("varOfVar", Arrays.asList("${key}")); parameters.put("priority", "low"); parameters.put("low-list", Arrays.asList("low-1", "low-2")); parameters.put("middle-list", Arrays.asList("middle-1", "middle-2")); parameters.put("high-list", Arrays.asList("high-1", "high-2")); } protected Environment environment; @BeforeEach public void setup() { environment = Mockito.mock(Environment.class); LegacyPropertyFactory.setEnvironment(environment); } @Test public void unknown() { MatcherAssert.assertThat(resolver.replace("prefix${xxx}suffix", parameters), Matchers.contains("prefix${xxx}suffix")); } @Test public void empty() { MatcherAssert.assertThat(resolver.replace("prefix${}suffix", parameters), Matchers.contains("prefix${}suffix")); } @Test public void notComplete() { MatcherAssert.assertThat(resolver.replace("prefix${suffix", parameters), Matchers.contains("prefix${suffix")); } @Test public void normal() { MatcherAssert.assertThat(resolver.replace("prefix.${key}.suffix", parameters), Matchers.contains("prefix.value.suffix")); } @Test public void disable() { MatcherAssert.assertThat(resolver.replace("prefix.\\${key}.suffix", parameters), Matchers.contains("prefix.${key}.suffix")); } @Test public void varOfVar() { MatcherAssert.assertThat(resolver.replace("prefix.${varOfVar}.suffix", parameters), Matchers.contains("prefix.value.suffix")); } @Test public void list() { MatcherAssert.assertThat(resolver.replace("prefix.${low-list}.suffix", parameters), Matchers.contains("prefix.low-1.suffix", "prefix.low-2.suffix")); } @Test public void multi_list() { MatcherAssert.assertThat(resolver.replace("prefix.${low-list}.${middle-list}.${high-list}.suffix", parameters), Matchers.contains( "prefix.low-1.middle-1.high-1.suffix", "prefix.low-1.middle-1.high-2.suffix", "prefix.low-1.middle-2.high-1.suffix", "prefix.low-1.middle-2.high-2.suffix", "prefix.low-2.middle-1.high-1.suffix", "prefix.low-2.middle-1.high-2.suffix", "prefix.low-2.middle-2.high-1.suffix", "prefix.low-2.middle-2.high-2.suffix")); } @Test public void nested() { MatcherAssert.assertThat(resolver.replace("prefix.${${priority}-list}.suffix", parameters), Matchers.contains("prefix.low-1.suffix", "prefix.low-2.suffix")); } @Test public void mixed() { MatcherAssert.assertThat( resolver.replace("prefix.${${priority}-list}.${key}.${high-list}.suffix ${xxx}", parameters), Matchers.contains( "prefix.low-1.value.high-1.suffix ${xxx}", "prefix.low-2.value.high-1.suffix ${xxx}", "prefix.low-1.value.high-2.suffix ${xxx}", "prefix.low-2.value.high-2.suffix ${xxx}")); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/parser/TestParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.parser; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import org.junit.Test; public class TestParser { @Test public void testRawParser() { Parser parser = Parser.findParser(Parser.CONTENT_TYPE_RAW); assertThat(parser.parse("world", "hello", true)).containsKey("hello").containsValue("world"); assertThat(parser.parse("world", "hello", false)).containsKey("hello").containsValue("world"); } @Test public void testPropertiesParser() { Parser parser = Parser.findParser(Parser.CONTENT_TYPE_PROPERTIES); assertThat(parser.parse("l1-1=3.0\n" + "l1-2=2.0", "hello", true)).containsKeys("hello.l1-1", "hello.l1-2") .containsValues("2.0", "3.0"); assertThat(parser.parse("l1-1=3.0\n" + "l1-2=2.0", "hello", false)).containsKeys("l1-1", "l1-2") .containsValues("2.0", "3.0"); } @Test public void testYamlParser() { Parser parser = Parser.findParser(Parser.CONTENT_TYPE_YAML); assertThat(parser.parse("l1-1: 3.0\n" + "l1-2: 2.0", "hello", true)).containsKeys("hello.l1-1", "hello.l1-2") .containsValues("2.0", "3.0"); assertThat(parser.parse("l1-1: 3.0\n" + "l1-2: 2.0", "false", false)).containsKeys("l1-1", "l1-2") .containsValues("2.0", "3.0"); } @Test public void testInvalidParser() { Throwable exception = catchThrowable(() -> Parser.findParser("unknown")); assertThat(exception).isInstanceOf(IllegalArgumentException.class); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityProperty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestPriorityProperty extends TestPriorityPropertyBase { String high = "ms.schema.op"; String middle = "ms.schema"; String low = "ms"; String[] keys = {high, middle, low}; @Test public void testLong() { PriorityProperty config = propertyFactory.getOrCreate(Long.class, -1L, -2L, keys); Assertions.assertEquals(-2L, (long) config.getValue()); updateLong(low, 1L, config); Assertions.assertEquals(1L, (long) config.getValue()); updateLong(middle, 2L, config); Assertions.assertEquals(2L, (long) config.getValue()); updateLong(high, 3L, config); Assertions.assertEquals(3L, (long) config.getValue()); updateLong(middle, null, config); Assertions.assertEquals(3L, (long) config.getValue()); updateLong(middle, 2L, config); updateLong(high, null, config); Assertions.assertEquals(2L, (long) config.getValue()); updateLong(middle, null, config); Assertions.assertEquals(1L, (long) config.getValue()); updateLong(low, null, config); Assertions.assertEquals(-2L, (long) config.getValue()); } @Test public void testInt() { PriorityProperty config = propertyFactory.getOrCreate(Integer.class, -1, -2, keys); Assertions.assertEquals(-2L, (int) config.getValue()); updateInt(low, 1, config); Assertions.assertEquals(1, (int) config.getValue()); updateInt(middle, 2, config); Assertions.assertEquals(2, (int) config.getValue()); updateInt(high, 3, config); Assertions.assertEquals(3, (int) config.getValue()); updateInt(middle, null, config); Assertions.assertEquals(3, (int) config.getValue()); updateInt(middle, 2, config); updateInt(high, null, config); Assertions.assertEquals(2, (int) config.getValue()); updateInt(middle, null, config); Assertions.assertEquals(1, (int) config.getValue()); updateInt(low, null, config); Assertions.assertEquals(-2, (int) config.getValue()); } private void updateFloat(String key, Float value, PriorityProperty config) { Mockito.when(environment.getProperty(key, Float.class)).thenReturn(value); config.updateValue(); } private void updateStr(String key, String value, PriorityProperty config) { Mockito.when(environment.getProperty(key)).thenReturn(value); config.updateValue(); } private void updateInt(String key, Integer value, PriorityProperty config) { Mockito.when(environment.getProperty(key, Integer.class)).thenReturn(value); config.updateValue(); } private void updateLong(String key, Long value, PriorityProperty config) { Mockito.when(environment.getProperty(key, Long.class)).thenReturn(value); config.updateValue(); } private void updateBoolean(String key, Boolean value, PriorityProperty config) { Mockito.when(environment.getProperty(key, Boolean.class)).thenReturn(value); config.updateValue(); } private void updateDouble(String key, Double value, PriorityProperty config) { Mockito.when(environment.getProperty(key, Double.class)).thenReturn(value); config.updateValue(); } @Test public void testString() { PriorityProperty config = propertyFactory.getOrCreate(String.class, null, "def", keys); Assertions.assertEquals("def", config.getValue()); updateStr(low, "1", config); Assertions.assertEquals("1", config.getValue()); updateStr(middle, "2", config); Assertions.assertEquals("2", config.getValue()); updateStr(high, "3", config); Assertions.assertEquals("3", config.getValue()); updateStr(middle, null, config); Assertions.assertEquals("3", config.getValue()); updateStr(middle, "2", config); updateStr(high, null, config); Assertions.assertEquals("2", config.getValue()); updateStr(middle, null, config); Assertions.assertEquals("1", config.getValue()); updateStr(low, null, config); Assertions.assertEquals("def", config.getValue()); } @Test public void testBoolean() { PriorityProperty config = propertyFactory.getOrCreate(Boolean.class, null, false, keys); Assertions.assertFalse(config.getValue()); updateBoolean(low, true, config); Assertions.assertTrue(config.getValue()); updateBoolean(middle, false, config); Assertions.assertFalse(config.getValue()); updateBoolean(high, true, config); Assertions.assertTrue(config.getValue()); updateBoolean(middle, false, config); Assertions.assertTrue(config.getValue()); updateBoolean(middle, false, config); updateBoolean(high, null, config); Assertions.assertFalse(config.getValue()); updateBoolean(middle, null, config); Assertions.assertTrue(config.getValue()); updateBoolean(low, null, config); Assertions.assertFalse(config.getValue()); } @Test public void testDouble() { PriorityProperty config = propertyFactory.getOrCreate(Double.class, null, -2.0, keys); Assertions.assertEquals(-2, config.getValue(), 0); updateDouble(low, 1D, config); Assertions.assertEquals(1, config.getValue(), 0); updateDouble(middle, 2D, config); Assertions.assertEquals(2, config.getValue(), 0); updateDouble(high, 3D, config); Assertions.assertEquals(3, config.getValue(), 0); updateDouble(middle, null, config); Assertions.assertEquals(3, config.getValue(), 0); updateDouble(middle, 2D, config); updateDouble(high, null, config); Assertions.assertEquals(2, config.getValue(), 0); updateDouble(middle, null, config); Assertions.assertEquals(1, config.getValue(), 0); updateDouble(low, null, config); Assertions.assertEquals(-2, config.getValue(), 0); } @Test public void testFloat() { PriorityProperty config = propertyFactory.getOrCreate(Float.class, null, -2.0f, keys); Assertions.assertEquals(-2, config.getValue(), 0); updateFloat(low, 1F, config); Assertions.assertEquals(1, config.getValue(), 0); updateFloat(middle, 2F, config); Assertions.assertEquals(2, config.getValue(), 0); updateFloat(high, 3F, config); Assertions.assertEquals(3, config.getValue(), 0); updateFloat(middle, null, config); Assertions.assertEquals(3, config.getValue(), 0); updateFloat(middle, 2F, config); updateFloat(high, null, config); Assertions.assertEquals(2, config.getValue(), 0); updateFloat(middle, null, config); Assertions.assertEquals(1, config.getValue(), 0); updateFloat(low, null, config); Assertions.assertEquals(-2, config.getValue(), 0); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestPriorityPropertyBase { protected PriorityPropertyManager priorityPropertyManager; protected PriorityPropertyFactory propertyFactory; protected Environment environment; @BeforeEach public void setup() { environment = Mockito.mock(Environment.class); LegacyPropertyFactory.setEnvironment(environment); propertyFactory = new PriorityPropertyFactory(environment); priorityPropertyManager = new PriorityPropertyManager(new ConfigObjectFactory(propertyFactory)); } @AfterEach public void teardown() { } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class TestPriorityPropertyFactory extends TestPriorityPropertyBase { @Test void should_not_create_multiple_instances_for_same_parameter() { PriorityProperty p1 = propertyFactory.getOrCreate(int.class, null, 0, "high", "low"); PriorityProperty p2 = propertyFactory.getOrCreate(int.class, null, 0, "high", "low"); assertThat(p1).isSameAs(p2); assertThat(propertyFactory.getProperties().count()).isEqualTo(1); } @Test void should_create_different_instances_for_different_parameter() { PriorityProperty p1 = propertyFactory.getOrCreate(int.class, null, 0, "high", "low"); PriorityProperty p2 = propertyFactory.getOrCreate(long.class, null, 0L, "high", "low"); assertThat(p1).isNotSameAs(p2); assertThat(propertyFactory.getProperties().count()).isEqualTo(2); } } ================================================ FILE: foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.config.priority; import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestPriorityPropertyManager extends TestPriorityPropertyBase { String high = "ms.schema.op"; String middle = "ms.schema"; String low = "ms"; String[] keys = {high, middle, low}; @InjectProperties(prefix = "root") public static class ConfigWithAnnotation { @InjectProperty(prefix = "override", keys = {"high", "low"}, defaultValue = "abc") public String strValue; } private void waitKeyForGC(PriorityPropertyManager priorityPropertyManager) { long maxTime = 10000; long currentTime = System.currentTimeMillis(); while (System.currentTimeMillis() - currentTime < maxTime) { if (priorityPropertyManager.getConfigObjectMap().isEmpty()) { break; } System.runFinalization(); System.gc(); try { Thread.yield(); Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } @Test public void testConfigurationsAreGCCollected() { long timeBegin = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { ConfigWithAnnotation configConfigObject = priorityPropertyManager.createConfigObject( ConfigWithAnnotation.class); Assertions.assertEquals("abc", configConfigObject.strValue); PriorityProperty configPriorityProperty = propertyFactory.getOrCreate(Long.class, -1L, -2L, keys); Assertions.assertEquals(-2L, (long) configPriorityProperty.getValue()); } } waitKeyForGC(priorityPropertyManager); Assertions.assertTrue(priorityPropertyManager.getConfigObjectMap().isEmpty()); System.out.println("Token : " + (System.currentTimeMillis() - timeBegin)); } } ================================================ FILE: foundations/foundation-config/src/test/resources/empty.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- ================================================ FILE: foundations/foundation-config/src/test/resources/m1.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- zq: test: okok ================================================ FILE: foundations/foundation-config/src/test/resources/mapping.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: 0 CSE_ENV_MAPPING: servicecomb.testmapping.key MY_SERVICES_ENDPOINT: - servicecomb.service.mapping.address - servicecomb.service1.mapping.address SERVICECOMB_ENV: - service_description.environment - service_description.environment.old ================================================ FILE: foundations/foundation-config/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- trace: handler: enabled: false sampler: percent: 0.5 metric: service: enable: false zq: - tlist: a - tlist: b - tlist: 1 validate: parameter: enabled: true returnValue: enabled: true apiInvoke: enabled: true shutDownHandler: enabled: true timeLimit: 30000 eureka: instance: preferIpAddress: true leaseRenewalIntervalInSeconds: 3 leaseExpirationDurationInSeconds: 5 client: serviceUrl: defaultZone: http://10.120.169.202:9980/ cse: config: client: serviceName: testDemo serverUri: https://10.22.87.59:30103 tenantName: csetest refreshMode: 1 refresh_interval: 10000 service_description: name: testDemo holder: b,c,d test: commonSeparatedString: a,b,c commonSeparatedStringHolder: ${holder} stringArray: - m - n ================================================ FILE: foundations/foundation-config/src/test/resources/test1.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: "1" trace: handler: enabled: false sampler: percent: 0.5 metric: service: enable: false validate: parameter: enabled: true returnValue: enabled: true apiInvoke: enabled: true shutDownHandler: enabled: true timeLimit: 30000 has: manager: url: 127.0.0.1 serializer: default: protostuff provider: service: name: ${spring.application.name} version: 1.2 group: prod protocols: rpc: serviceLevel: interface dsf: # ip: "10.57.65.225" port: 7450 eureka: instance: preferIpAddress: true leaseRenewalIntervalInSeconds: 3 leaseExpirationDurationInSeconds: 5 client: serviceUrl: defaultZone: http://10.120.169.202:9980/ ================================================ FILE: foundations/foundation-config/src/test/resources/test2.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: 0 dsf: serializer: kryo: enabled: true referencesRequired: false registrationRequired: false ================================================ FILE: foundations/foundation-metrics/pom.xml ================================================ 4.0.0 org.apache.servicecomb foundations 3.4.0-SNAPSHOT foundation-metrics Java Chassis::Foundations::Metrics org.apache.servicecomb foundation-common io.micrometer micrometer-core org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics; import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.micrometer.core.instrument.MeterRegistry; public class MetricsBootstrap { private static final Logger LOGGER = LoggerFactory.getLogger(MetricsBootstrap.class); private final MetricsBootstrapConfig config; private MeterRegistry meterRegistry; private EventBus eventBus; private ScheduledExecutorService executorService; private List metricsInitializers; @Autowired public MetricsBootstrap(MetricsBootstrapConfig config) { this.config = config; } @Autowired public void setMetricsInitializers(List metricsInitializers) { this.metricsInitializers = metricsInitializers; } @Autowired public void setMeterRegistry(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; } public void start(EventBus eventBus) { this.eventBus = eventBus; this.executorService = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder() .setNameFormat("metrics-poller-%d") .build()); metricsInitializers.forEach(initializer -> initializer.init(this.meterRegistry, eventBus, config)); startPoll(); } public void shutdown() { if (executorService != null) { executorService.shutdown(); } Collections.reverse(metricsInitializers); metricsInitializers.forEach(MetricsInitializer::destroy); } protected void startPoll() { executorService.scheduleAtFixedRate(this::pollMeters, config.getMsPollInterval(), config.getMsPollInterval(), TimeUnit.MILLISECONDS); } public synchronized void pollMeters() { metricsInitializers.forEach(initializer -> { if (initializer instanceof PeriodMeter) { ((PeriodMeter) initializer).poll(System.currentTimeMillis(), config.getMsPollInterval()); } }); try { PolledEvent polledEvent = new PolledEvent(meterRegistry.getMeters()); eventBus.post(polledEvent); } catch (Throwable e) { LOGGER.error("poll meters error. ", e); } } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics; import org.springframework.core.env.Environment; public class MetricsBootstrapConfig { public static final String METRICS_WINDOW_TIME = "servicecomb.metrics.window_time"; public static final String CONFIG_LATENCY_DISTRIBUTION = "servicecomb.metrics.invocation.latencyDistribution"; public static final String CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN = "servicecomb.metrics.publisher.defaultLog.invocation.latencyDistribution.minScopeLength"; public static final int DEFAULT_METRICS_WINDOW_TIME = 300_000; private long msPollInterval; private String latencyDistribution; private int minScopeLength; private Environment environment; public MetricsBootstrapConfig(Environment environment) { this.environment = environment; msPollInterval = environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME); if (msPollInterval < 1000) { msPollInterval = 1000; } latencyDistribution = environment.getProperty(CONFIG_LATENCY_DISTRIBUTION, String.class); minScopeLength = environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7); } public Environment getEnvironment() { return environment; } public long getMsPollInterval() { return msPollInterval; } public String getLatencyDistribution() { return latencyDistribution; } public int getMinScopeLength() { return minScopeLength; } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; public interface MetricsInitializer { default int getOrder() { return 0; } void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config); default void destroy() { } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics; import java.util.List; import io.micrometer.core.instrument.Meter; public class PolledEvent { private List meters; public PolledEvent(List meters) { this.meters = meters; } public List getMeters() { return meters; } public void setMeters(List meters) { this.meters = meters; } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.health; public class HealthCheckResult { private boolean healthy; private String information; private String extraData; private long timestamp; public boolean isHealthy() { return healthy; } public String getInformation() { return information; } public String getExtraData() { return extraData; } public long getTimestamp() { return timestamp; } public HealthCheckResult() { } public HealthCheckResult(boolean healthy, String information, String extraData) { this(); this.healthy = healthy; this.information = information; this.extraData = extraData; this.timestamp = System.currentTimeMillis(); } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthChecker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.health; public interface HealthChecker { String getName(); HealthCheckResult check(); } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckerManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.health; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; public class HealthCheckerManager { private final Map healthCheckers; private static final HealthCheckerManager INSTANCE = new HealthCheckerManager(); public static HealthCheckerManager getInstance() { return INSTANCE; } private HealthCheckerManager() { this.healthCheckers = new ConcurrentHashMap<>(); List checkers = SPIServiceUtils.getAllService(HealthChecker.class); for (HealthChecker checker : checkers) { register(checker); } } public void register(HealthChecker checker) { healthCheckers.put(checker.getName(), checker); } public void unregister(String name) { healthCheckers.remove(name); } public Map check() { return healthCheckers.entrySet().stream().collect(Collectors.toMap(Entry::getKey, e -> e.getValue().check())); } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.meter; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LatencyDistributionConfig { public static final Long MAX_LATENCY = 60 * 60 * 1000L; private static final Logger LOGGER = LoggerFactory.getLogger(LatencyDistributionConfig.class); private final List scopeConfigs = new ArrayList<>(); /** * * @param config scope definition, time unit is milliseconds, eg:0,1,10 */ public LatencyDistributionConfig(String config) { if (StringUtils.isEmpty(config)) { return; } config = config.trim() + "," + MAX_LATENCY; String[] array = config.split("\\s*,+\\s*"); try { for (int idx = 0; idx < array.length - 1; idx++) { long msMin = Long.parseLong(array[idx]); long msMax = Long.parseLong(array[idx + 1]); if (msMin >= msMax) { String msg = String.format("invalid latency scope, min=%s, max=%s.", array[idx], array[idx + 1]); throw new IllegalStateException(msg); } LatencyScopeConfig latencyScopeConfig = new LatencyScopeConfig(msMin, msMax); scopeConfigs.add(latencyScopeConfig); } } catch (Throwable e) { LOGGER.error("Failed to parse latencyDistributionConfig, value={}", config, e); throw e; } } public List getScopeConfigs() { return scopeConfigs; } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.meter; public class LatencyScopeConfig { // [min, max) // even max equals Long.MAX_VALUE, still not include it // because it will never happened in real cases private final long msMin; private final long msMax; public LatencyScopeConfig(long msMin, long msMax) { this.msMin = msMin; this.msMax = msMax; } public long getMsMin() { return msMin; } public long getMsMax() { return msMax; } @Override public String toString() { return "LatencyScopeConfig[" + msMin + ", " + msMax + ')'; } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.meter; public interface PeriodMeter { void poll(long msNow, long secondInterval); } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish; import io.micrometer.core.instrument.Tag; public class DefaultTagFinder implements TagFinder { private final String tagKey; private boolean skipOnNull; public DefaultTagFinder(String tagKey) { this.tagKey = tagKey; } public DefaultTagFinder(String tagKey, boolean skipOnNull) { this.tagKey = tagKey; this.skipOnNull = skipOnNull; } @Override public boolean skipOnNull() { return skipOnNull; } @Override public String getTagKey() { return tagKey; } @Override public Tag find(Iterable tags) { for (Tag tag : tags) { if (tag.getKey().equals(tagKey)) { return tag; } } return null; } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class MeasurementGroupConfig { // key is measurement id name private final Map> groups = new HashMap<>(); public MeasurementGroupConfig() { } public MeasurementGroupConfig(String idName, Object... tagNameOrFinders) { addGroup(idName, tagNameOrFinders); } public void addGroup(String idName, Object... tagNameOrFinders) { groups.put(idName, Arrays .asList(tagNameOrFinders) .stream() .map(TagFinder::build) .collect(Collectors.toList())); } public List findTagFinders(String idName) { return groups.get(idName); } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter.Id; public class MeasurementNode implements Comparable { private final String name; private final Id id; private final List measurements = new ArrayList<>(); private Map children; public MeasurementNode(String name, Id id, Map children) { this.name = name; this.id = id; this.children = children; } public String getName() { return this.name; } public Id getId() { return id; } public Map getChildren() { return children; } public MeasurementNode findChild(String childName) { if (children == null) { return null; } return children.get(childName); } public MeasurementNode findChild(String... childNames) { MeasurementNode node = this; for (String childName : childNames) { if (node == null) { return null; } node = node.findChild(childName); } return node; } public MeasurementNode addChild(String childName, Id id, Measurement measurement) { if (children == null) { children = new LinkedHashMap<>(); } MeasurementNode node = children.computeIfAbsent(childName, name -> new MeasurementNode(name, id, null)); if (measurement != null) { node.addMeasurement(measurement); } return node; } public List getMeasurements() { return measurements; } public void addMeasurement(Measurement measurement) { measurements.add(measurement); } public double summary() { double result = 0; for (Measurement measurement : measurements) { result += measurement.getValue(); } return result; } @Override public int compareTo(MeasurementNode o) { return this.name.compareTo(o.name); } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.Statistic; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.distribution.CountAtBucket; import io.micrometer.core.instrument.distribution.HistogramSnapshot; // like select * from meters group by ...... // but output a tree not a table public class MeasurementTree extends MeasurementNode { public MeasurementTree() { super(null, null, null); } // groupConfig: // key: id name // value: id tag keys // only id name exists in groupConfig will accept, others will be ignored public void from(Iterator meters, MeasurementGroupConfig groupConfig) { meters.forEachRemaining(meter -> { // This code snip is not very good design. But DistributionSummary is quite special. if (meter instanceof DistributionSummary distributionSummary) { HistogramSnapshot snapshot = distributionSummary.takeSnapshot(); CountAtBucket[] countAtBuckets = snapshot.histogramCounts(); List distributions = new ArrayList<>(countAtBuckets.length); for (CountAtBucket countAtBucket : countAtBuckets) { final double value = countAtBucket.count(); distributions.add(new Measurement(() -> value, Statistic.COUNT)); } from(meter.getId(), distributions, groupConfig); return; } Iterable measurements = meter.measure(); from(meter.getId(), measurements, groupConfig); }); } public void from(Id id, Iterable measurements, MeasurementGroupConfig groupConfig) { for (Measurement measurement : measurements) { MeasurementNode node = addChild(id.getName(), id, measurement); List tagFinders = groupConfig.findTagFinders(id.getName()); if (tagFinders == null) { continue; } for (TagFinder tagFinder : tagFinders) { Tag tag = tagFinder.find(id.getTags()); if (tag == null) { if (tagFinder.skipOnNull()) { break; } throw new IllegalStateException( String.format("tag key \"%s\" not exist in %s", tagFinder.getTagKey(), id)); } node = node.addChild(tag.getValue(), id, measurement); } node.addChild(measurement.getStatistic().name(), id, measurement); } } } ================================================ FILE: foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish; import io.micrometer.core.instrument.Tag; public interface TagFinder { static TagFinder build(Object obj) { if (String.class.isInstance(obj)) { return new DefaultTagFinder((String) obj); } if (TagFinder.class.isInstance(obj)) { return (TagFinder) obj; } throw new IllegalArgumentException( "only support String or TagFinder, but got " + (obj == null ? "null" : obj.getClass().getName())); } default boolean skipOnNull() { return false; } String getTagKey(); // read target tag from tags // return directly or do some change and then return Tag find(Iterable tags); } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; import mockit.Deencapsulation; public class TestMetricsBootstrap { MetricsBootstrap bootstrap; EventBus eventBus = new EventBus(); Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); MetricsBootstrapConfig config = new MetricsBootstrapConfig(environment); bootstrap = new MetricsBootstrap(config); bootstrap.setMetricsInitializers(List.of()); } @Test public void loadMetricsInitializers() { List initList = new ArrayList<>(); MetricsInitializer metricsInitializer = new MetricsInitializer() { @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { initList.add(this); } }; bootstrap.setMetricsInitializers(Arrays.asList(metricsInitializer, metricsInitializer)); bootstrap.start(eventBus); bootstrap.shutdown(); MatcherAssert.assertThat(initList, Matchers.contains(metricsInitializer, metricsInitializer)); } @Test public void shutdown() { ScheduledExecutorService scheduledExecutorService = Mockito.mock(ScheduledExecutorService.class); List destroyList = new ArrayList<>(); MetricsInitializer initializer1 = new MetricsInitializer() { @Override public int getOrder() { return 1; } @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { } @Override public void destroy() { destroyList.add(this); } }; MetricsInitializer initializer2 = new MetricsInitializer() { @Override public int getOrder() { return 2; } @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { } @Override public void destroy() { destroyList.add(this); } }; bootstrap.setMetricsInitializers(Arrays.asList(initializer1, initializer2)); Deencapsulation.setField(bootstrap, "executorService", scheduledExecutorService); bootstrap.shutdown(); MatcherAssert.assertThat(destroyList, Matchers.contains(initializer2, initializer1)); } @Test public void shutdown_notStart() { Assertions.assertNull(Deencapsulation.getField(bootstrap, "executorService")); // should not throw exception bootstrap.shutdown(); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/health/TestHealthCheckerManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.health; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestHealthCheckerManager { private final HealthChecker good = new HealthChecker() { @Override public String getName() { return "testBad"; } @Override public HealthCheckResult check() { return new HealthCheckResult(false, "bad", "bad component"); } }; private final HealthChecker bad = new HealthChecker() { @Override public String getName() { return "testGood"; } @Override public HealthCheckResult check() { return new HealthCheckResult(true, "good", "good component"); } }; @BeforeEach public void reset() { HealthCheckerManager.getInstance().unregister(good.getName()); HealthCheckerManager.getInstance().unregister(bad.getName()); } @Test public void checkResultCount_None() { Map results = HealthCheckerManager.getInstance().check(); Assertions.assertEquals(0, results.size()); } @Test public void checkResultCount_One() { HealthCheckerManager.getInstance().register(good); Map results = HealthCheckerManager.getInstance().check(); Assertions.assertEquals(1, results.size()); } @Test public void checkResultCount_Both() { HealthCheckerManager.getInstance().register(good); HealthCheckerManager.getInstance().register(bad); Map results = HealthCheckerManager.getInstance().check(); Assertions.assertEquals(2, results.size()); } @Test public void checkGoodResult() { HealthCheckerManager.getInstance().register(good); HealthCheckerManager.getInstance().register(bad); HealthCheckResult result = HealthCheckerManager.getInstance().check().get("testGood"); Assertions.assertTrue(result.isHealthy()); Assertions.assertEquals("good", result.getInformation()); Assertions.assertEquals("good component", result.getExtraData()); } @Test public void checkBadResult() { HealthCheckerManager.getInstance().register(good); HealthCheckerManager.getInstance().register(bad); HealthCheckResult result = HealthCheckerManager.getInstance().check().get("testBad"); Assertions.assertFalse(result.isHealthy()); Assertions.assertEquals("bad", result.getInformation()); Assertions.assertEquals("bad component", result.getExtraData()); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.meter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestLatencyDistributionConfig { @Test public void testValidProperty() { String validProperty1 = "0,1,2,10"; String validProperty2 = "0,1, 2 , 10 "; String validProperty3 = "0,1,2,10,"; LatencyDistributionConfig config1 = new LatencyDistributionConfig(validProperty1); LatencyDistributionConfig config2 = new LatencyDistributionConfig(validProperty2); LatencyDistributionConfig config3 = new LatencyDistributionConfig(validProperty3); Assertions.assertEquals(4, config1.getScopeConfigs().size()); Assertions.assertEquals(4, config2.getScopeConfigs().size()); Assertions.assertEquals(4, config3.getScopeConfigs().size()); } @Test public void testInValidProperty1() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { LatencyDistributionConfig latencyDistributionConfig = new LatencyDistributionConfig("2,1,10"); Assertions.assertEquals(0, latencyDistributionConfig.getScopeConfigs().size()); }); Assertions.assertEquals("invalid latency scope, min=2, max=1.", exception.getMessage()); } @Test public void testInValidProperty2() { NumberFormatException exception = Assertions.assertThrows(NumberFormatException.class, () -> { LatencyDistributionConfig latencyDistributionConfig = new LatencyDistributionConfig("a,1,10"); Assertions.assertEquals(0, latencyDistributionConfig.getScopeConfigs().size()); }); Assertions.assertEquals("For input string: \"a\"", exception.getMessage()); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish.spectator; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.micrometer.core.instrument.Tag; public class TestDefaultTagFinder { TagFinder finder = new DefaultTagFinder("key"); @Test public void getTagKey() { Assertions.assertEquals("key", finder.getTagKey()); } @Test public void readSucc() { Tag tag = Tag.of("key", "value"); List tags = Arrays.asList(Tag.of("t1", "t1v"), tag); Assertions.assertSame(tag, finder.find(tags)); } @Test public void readFail() { List tags = Arrays.asList(Tag.of("t1", "t1v")); Assertions.assertNull(finder.find(tags)); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish.spectator; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import mockit.Deencapsulation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMeasurementGroupConfig { MeasurementGroupConfig config = new MeasurementGroupConfig(); Map> groups = Deencapsulation.getField(config, "groups"); @Test public void defaultConstruct() { Assertions.assertTrue(groups.isEmpty()); } @Test public void constructAddGroup() { config = new MeasurementGroupConfig("id", "tag1"); groups = Deencapsulation.getField(config, "groups"); MatcherAssert.assertThat(groups.keySet(), Matchers.contains("id")); MatcherAssert.assertThat(groups.get("id").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag1")); } @Test public void addGroup() { config.addGroup("id1", "tag1.1", "tag1.2"); config.addGroup("id2", "tag2.1", "tag2.2"); MatcherAssert.assertThat(groups.keySet(), Matchers.containsInAnyOrder("id2", "id1")); MatcherAssert.assertThat(groups.get("id1").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag1.1", "tag1.2")); MatcherAssert.assertThat(groups.get("id2").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag2.1", "tag2.2")); } @Test public void findTagReaders() { config.addGroup("id1", "tag1.1", "tag1.2"); config.addGroup("id2", "tag2.1", "tag2.2"); MatcherAssert.assertThat(config.findTagFinders("id2").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag2.1", "tag2.2")); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish.spectator; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.Meter.Type; import io.micrometer.core.instrument.Tags; import mockit.Expectations; import mockit.Mocked; public class TestMeasurementNode { Id id = new Id("name", Tags.empty(), null, null, Type.OTHER); MeasurementNode node = new MeasurementNode("name", id, null); @Test public void getName() { Assertions.assertEquals("name", node.getName()); } @Test public void getChildren() { Map children = new HashMap<>(); node = new MeasurementNode("name", id, children); Assertions.assertSame(children, node.getChildren()); } @Test public void findChild_noChildren() { Assertions.assertNull(node.findChild("child")); } @Test public void findChild_multiLevel_noMiddleChildren(@Mocked Measurement measurement) { MeasurementNode c1 = node.addChild("c1", id, measurement); c1.addChild("c2", id, measurement); Assertions.assertNull(node.findChild("c1_notExist", "c2")); } @Test public void findChild_multiLevel_ok(@Mocked Measurement measurement) { MeasurementNode c1 = node.addChild("c1", id, measurement); MeasurementNode c2 = c1.addChild("c2", id, measurement); Assertions.assertSame(c2, node.findChild("c1", "c2")); } @Test public void addChild(@Mocked Measurement measurement) { MeasurementNode c1 = node.addChild("c1", id, measurement); MeasurementNode c2 = node.addChild("c2", id, measurement); Assertions.assertSame(c1, node.findChild("c1")); Assertions.assertSame(c2, node.findChild("c2")); } @Test public void getMeasurements(@Mocked Measurement measurement) { node.addMeasurement(measurement); MatcherAssert.assertThat(node.getMeasurements(), Matchers.contains(measurement)); } @Test public void summary(@Mocked Measurement measurement) { new Expectations() { { measurement.getValue(); result = 10; } }; node.addMeasurement(measurement); node.addMeasurement(measurement); Assertions.assertEquals(20, node.summary(), 0); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish.spectator; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Statistic; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class TestMeasurementTree { MeasurementTree tree = new MeasurementTree(); MeterRegistry registry = new SimpleMeterRegistry(); Timer timer; @Before public void setup() { timer = registry.timer("id", "g1", "g1v", "g2", "g2v", "t3", "t3v", "t4", "t4v"); registry.counter("id_notCare"); } @Test public void from() { timer.record(10, TimeUnit.SECONDS); timer.record(2, TimeUnit.SECONDS); MeasurementGroupConfig config = new MeasurementGroupConfig("id", "g1", "g2"); tree.from(registry.getMeters().iterator(), config); Assertions.assertEquals(2, tree.getChildren().size()); MeasurementNode node = tree.findChild("id", "g1v", "g2v"); Assertions.assertEquals(2d, node.findChild(Statistic.COUNT.name()).getMeasurements().get(0).getValue(), 0); Assertions.assertEquals(12d, node.findChild(Statistic.TOTAL_TIME.name()).getMeasurements().get(0).getValue(), 0); Assertions.assertEquals(0d, tree.findChild("id_notCare").summary(), 0); } @Test public void from_withSkipOnNull() { try { MeasurementGroupConfig config = new MeasurementGroupConfig("id", new DefaultTagFinder("notExist", true)); tree.from(registry.getMeters().iterator(), config); } catch (Exception e) { Assertions.fail("should not throw exception"); } } @Test public void from_failed() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { MeasurementGroupConfig config = new MeasurementGroupConfig("id", "notExist"); tree.from(registry.getMeters().iterator(), config); }); Assertions.assertEquals( "tag key \"notExist\" not exist in MeterId{name='id', tags=[tag(g1=g1v),tag(g2=g2v),tag(t3=t3v),tag(t4=t4v)]}", exception.getMessage()); } } ================================================ FILE: foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.metrics.publish.spectator; import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTagFinder { @Test public void buildFromString() { String name = "key"; TagFinder finder = TagFinder.build(name); Assertions.assertEquals(name, finder.getTagKey()); Assertions.assertEquals(DefaultTagFinder.class, finder.getClass()); } @Test public void buildFromTagFinder() { TagFinder finder = new DefaultTagFinder("key"); Assertions.assertSame(finder, TagFinder.build(finder)); DefaultTagFinder tagFinder = new DefaultTagFinder("key", true); Assertions.assertSame(tagFinder, TagFinder.build(tagFinder)); } @Test public void buildFromInvalidType() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> TagFinder.build(1)); Assertions.assertEquals("only support String or TagFinder, but got " + Integer.class.getName(), exception.getMessage()); } @Test public void buildFromNull() { IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> TagFinder.build(null)); Assertions.assertEquals("only support String or TagFinder, but got null", exception.getMessage()); } } ================================================ FILE: foundations/foundation-protobuf/README.md ================================================ # Overview There are many existing protobuf codecs, but all of them are not suit for us. ServiceComb data model is just POJO, not bind to any codec mechanism, so we had to create new one: | | ServiceComb | protobuf | protostuff | jackson | | -------------------------------------------------------- | :-----------: | :------: | :------------: | :-----: | | generate code | no need
just POJO | must
100+ lines IDL, generate 10000+ java code | no need
POJO with Annotation(eg:@Tag) | no need | | support null element in repeated field | no | no | not compatible | no | | support "oneOf" | no | yes | no | no | | support "packed" | yes | yes | no | no | | support "map" | yes | yes | not compatible | no | | support "any" | yes | yes | not compatible | no | | support "any" not defined in IDL | yes
extend based on "any" mechanism | no | not compatible | no | | support field: `List>` | yes | no | not compatible | no | | support field: `List>` | yes | no | not compatible | no | | support field: `Map>` | yes | no | not compatible | no | | support field: `Map>` | yes | no | not compatible | no | | support field: array | yes | no | not compatible | no | | support generic POJO type, eg:`CustomGeneric` | yes | no | no | no | | serialize/deserialize based on IDL | yes | no | no | yes | | serialize not repeated number field ignore default value | yes | yes | no | no | | serialize from map model | yes | no | no | no | | deserialize to map model | yes | no | no | no | # Usage ## Create factory one factory instance globally is enough ```java ProtoMapperFactory factory = new ProtoMapperFactory(); ``` ## Load proto definition create mapper instance for each proto definition - load from classpath ```java ProtoMapper protoMapper = factory.createFromName("protobuf.proto"); ``` - load from proto content ```java ProtoMapper protoMapper = factory.createFromContent(protoContent); ``` ## Serialize serializer is reusable and thread safe Assuming you have a proto definition ```proto message User { string name = 1; } ``` and a POJO class ```java public class User { private String name; // getter and setter } ``` RootSerializer serializer = protoMapper.createRootSerializer("User", User.class); User user = new User(); user.setName("userName"); byte[] pojoBytes= serializer.serialize(user); Map map = new HashMap<>(); map.put("name", "userName"); byte[] mapBytes = serializer.serialize(map); ```java // pojoBytes equals mapBytes ``` ## Deserialize deserializer is reusable and thread safe ```java RootDeserializer pojoDeserializer = protoMapper.createRootDeserializer("User", User.class); RootDeserializer> mapDeserializer = protoMapper.createRootDeserializer("User", Map.class); User user = pojoDeserializer.deserialize(bytes); Map map = mapDeserializer.deserialize(bytes); ``` # Performance ``` 1.protobuf in our real scenes business model never bind to transport, and can switch between different transports dynamically that means if we choose standard protobuf, must build protobuf models from business models each time so should be much slower than the test results 2.protoStuff some scenes, there is no field but have getter or setter, so we can not use unsafe to access field so we disable protoStuff unsafe feature for repeated fields, protoStuff have better performance, but not compatible to protobuf 3.jackson not support map/any/recursive, ignore related fields 4.serialize result size ScbStrong/ScbWeak/Protobuf have the same and smaller size, because skip all default/null value Empty: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 519 515 240 288 1242 serialize len : 36 0 0 0 56 deserialize time(ms): 161 69 10 516 486 deserialize->serialize len: 36 0 0 0 56 serialize+deserialize(ms) : 680 584 250 804 1728 Scalars: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 557 529 328 262 1357 serialize len : 56 24 24 24 76 deserialize time(ms): 181 141 115 527 504 deserialize->serialize len: 56 24 24 24 76 serialize+deserialize(ms) : 738 670 443 789 1861 Pojo: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 571 574 276 309 1304 serialize len : 46 10 10 10 66 deserialize time(ms): 230 69 112 668 537 deserialize->serialize len: 46 10 10 10 66 serialize+deserialize(ms) : 801 643 388 977 1841 SimpleList: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 590 609 296 637 1320 serialize len : 68 32 32 32 88 deserialize time(ms): 233 105 122 2226 541 deserialize->serialize len: 68 32 32 32 88 serialize+deserialize(ms) : 823 714 418 2863 1861 PojoList: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 609 632 319 2777 1407 serialize len : 56 20 20 20 76 deserialize time(ms): 244 134 173 2287 679 deserialize->serialize len: 56 20 20 20 76 serialize+deserialize(ms) : 853 766 492 5064 2086 Map: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 746 772 491 1079 1298 serialize len : 92 54 54 54 56 deserialize time(ms): 522 427 468 1031 422 deserialize->serialize len: 92 54 54 54 56 serialize+deserialize(ms) : 1268 1199 959 2110 1720 Mixed: Protostuff ScbStrong ScbWeak Protobuf Jackson serialize time(ms) : 1686 1999 2034 2112 2537 serialize len : 479 505 505 505 489 deserialize time(ms): 1969 2154 2923 2984 3316 deserialize->serialize len: 479 505 505 505 489 serialize+deserialize(ms) : 3655 4153 4957 5096 5853 ``` ================================================ FILE: foundations/foundation-protobuf/pom.xml ================================================ foundations org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 foundation-protobuf Java Chassis::Foundations::Protobuf org.apache.servicecomb foundation-common io.protostuff protostuff-parser io.protostuff protostuff-runtime io.protostuff protostuff-core com.google.code.findbugs jsr305 com.google.protobuf protobuf-java test com.fasterxml.jackson.dataformat jackson-dataformat-protobuf test org.apache.servicecomb foundation-test-scaffolding org.xolstice.maven.plugins protobuf-maven-plugin ${protobuf-maven-plugin.version} com.google.protobuf:protoc:${protoc3-maven-plugin.version}:exe:${os.detected.classifier} true grpc-java io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc-java-plugin.version}:exe:${os.detected.classifier} src/test/resources **/jacksonRoot.proto **/method.proto **/model.proto generate-test-sources test-compile ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java ================================================ //======================================================================== //Copyright 2007-2010 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; import static io.protostuff.WireFormat.TAG_TYPE_BITS; import static io.protostuff.WireFormat.WIRETYPE_END_GROUP; import static io.protostuff.WireFormat.WIRETYPE_FIXED32; import static io.protostuff.WireFormat.WIRETYPE_FIXED64; import static io.protostuff.WireFormat.WIRETYPE_LENGTH_DELIMITED; import static io.protostuff.WireFormat.WIRETYPE_START_GROUP; import static io.protostuff.WireFormat.WIRETYPE_VARINT; import static io.protostuff.WireFormat.getTagFieldNumber; import static io.protostuff.WireFormat.getTagWireType; import static io.protostuff.WireFormat.makeTag; import java.io.IOException; import java.nio.ByteBuffer; import com.fasterxml.jackson.databind.util.ArrayBuilders; import io.protostuff.StringSerializer.STRING; /** * Forked and modified from protostuff * * Reads and decodes protocol buffer message fields from an internal byte array buffer. This object is re-usable via * doing a reset on the byte array position and length. This is used internally by {@link IOUtil} where it catches * {@link ArrayIndexOutOfBoundsException} when a message is truncated. * * @author David Yu * @created Jun 22, 2010 */ public final class ByteArrayInputEx implements InputEx { private final byte[] buffer; private int offset, limit, lastTag = 0; private int packedLimit = 0; private ArrayBuilders arrayBuilders; public ByteArrayInputEx(byte[] buffer) { this(buffer, 0, buffer.length); } public ByteArrayInputEx(byte[] buffer, int offset, int len) { this.buffer = buffer; this.offset = offset; this.limit = offset + len; } /** * Return true if currently reading packed field */ public boolean isCurrentFieldPacked() { return packedLimit != 0 && packedLimit != offset; } /** * Attempt to read a field tag, returning zero if we have reached EOF. Protocol message parsers use this to read * tags, since a protocol message may legally end wherever a tag occurs, and zero is not a valid tag number. */ public int readTag() throws IOException { if (offset == limit) { lastTag = 0; return 0; } final int tag = readRawVarint32(); if (tag >>> TAG_TYPE_BITS == 0) { // If we actually read zero, that's not a valid tag. throw ProtobufException.invalidTag(); } lastTag = tag; return tag; } /** * Verifies that the last call to readTag() returned the given tag value. This is used to verify that a nested group * ended with the correct end tag. * * @throws ProtobufException * {@code value} does not match the last tag. */ public void checkLastTagWas(final int value) throws ProtobufException { if (lastTag != value) { throw ProtobufException.invalidEndTag(); } } /** * Reads and discards a single field, given its tag value. * * @return {@code false} if the tag is an endgroup tag, in which case nothing is skipped. Otherwise, returns * {@code true}. */ public boolean skipField(final int tag) throws IOException { switch (getTagWireType(tag)) { case WIRETYPE_VARINT: readInt32(); return true; case WIRETYPE_FIXED64: readRawLittleEndian64(); return true; case WIRETYPE_LENGTH_DELIMITED: final int size = readRawVarint32(); if (size < 0) { throw ProtobufException.negativeSize(); } offset += size; return true; case WIRETYPE_START_GROUP: skipMessage(); checkLastTagWas(makeTag(getTagFieldNumber(tag), WIRETYPE_END_GROUP)); return true; case WIRETYPE_END_GROUP: return false; case WIRETYPE_FIXED32: readRawLittleEndian32(); return true; default: throw ProtobufException.invalidWireType(); } } /** * Reads and discards an entire message. This will read either until EOF or until an endgroup tag, whichever comes * first. */ public void skipMessage() throws IOException { while (true) { final int tag = readTag(); if (tag == 0 || !skipField(tag)) { return; } } } @Override public void handleUnknownField(int fieldNumber) throws IOException { skipField(lastTag); } @Override public int readFieldNumber() throws IOException { if (offset == limit) { lastTag = 0; return 0; } // are we reading packed field? if (isCurrentFieldPacked()) { if (packedLimit < offset) { throw ProtobufException.misreportedSize(); } // Return field number while reading packed field return lastTag >>> TAG_TYPE_BITS; } packedLimit = 0; final int tag = readRawVarint32(); final int fieldNumber = tag >>> TAG_TYPE_BITS; if (fieldNumber == 0) { // If we actually read zero, that's not a valid tag. throw ProtobufException.invalidTag(); } lastTag = tag; return fieldNumber; } /** * Check if this field have been packed into a length-delimited field. If so, update internal state to reflect that * packed fields are being read. * */ private void checkIfPackedField() throws IOException { // Do we have the start of a packed field? if (packedLimit == 0 && getTagWireType(lastTag) == WIRETYPE_LENGTH_DELIMITED) { final int length = readRawVarint32(); if (length < 0) { throw ProtobufException.negativeSize(); } if (offset + length > limit) { throw ProtobufException.misreportedSize(); } this.packedLimit = this.offset + length; } } /** * Read a {@code double} field value from the internal buffer. */ @Override public double readDouble() { return Double.longBitsToDouble(readRawLittleEndian64()); } /** * Read a {@code float} field value from the internal buffer. */ @Override public float readFloat() { return Float.intBitsToFloat(readRawLittleEndian32()); } /** * Read a {@code uint64} field value from the internal buffer. */ @Override public long readUInt64() throws IOException { return readRawVarint64(); } /** * Read an {@code int64} field value from the internal buffer. */ @Override public long readInt64() throws IOException { return readRawVarint64(); } /** * Read an {@code int32} field value from the internal buffer. */ @Override public int readInt32() throws IOException { return readRawVarint32(); } /** * Read a {@code fixed64} field value from the internal buffer. */ @Override public long readFixed64() { return readRawLittleEndian64(); } /** * Read a {@code fixed32} field value from the internal buffer. */ @Override public int readFixed32() { return readRawLittleEndian32(); } /** * Read a {@code bool} field value from the internal buffer. */ @Override public boolean readBool() { return buffer[offset++] != 0; } /** * Read a {@code uint32} field value from the internal buffer. */ @Override public int readUInt32() throws IOException { return readRawVarint32(); } /** * Read an enum field value from the internal buffer. Caller is responsible for converting the numeric value to an * actual enum. */ @Override public int readEnum() throws IOException { return readRawVarint32(); } /** * Read an {@code sfixed32} field value from the internal buffer. */ @Override public int readSFixed32() { return readRawLittleEndian32(); } /** * Read an {@code sfixed64} field value from the internal buffer. */ @Override public long readSFixed64() { return readRawLittleEndian64(); } /** * Read an {@code sint32} field value from the internal buffer. */ @Override public int readSInt32() throws IOException { final int n = readRawVarint32(); return (n >>> 1) ^ -(n & 1); } /** * Read an {@code sint64} field value from the internal buffer. */ @Override public long readSInt64() throws IOException { final long n = readRawVarint64(); return (n >>> 1) ^ -(n & 1); } @Override public String readString() throws IOException { final int length = readRawVarint32(); if (length < 0) { throw ProtobufException.negativeSize(); } if (offset + length > limit) { throw ProtobufException.misreportedSize(); } final int offset = this.offset; this.offset += length; return STRING.deser(buffer, offset, length); } @Override public ByteString readBytes() throws IOException { return ByteString.wrap(readByteArray()); } @Override public byte[] readByteArray() throws IOException { final int length = readRawVarint32(); if (length < 0) { throw ProtobufException.negativeSize(); } if (offset + length > limit) { throw ProtobufException.misreportedSize(); } final byte[] copy = new byte[length]; System.arraycopy(buffer, offset, copy, 0, length); offset += length; return copy; } @Override public T mergeObject(T value, final SchemaReader schema) throws IOException { final int length = readRawVarint32(); if (length < 0) { throw ProtobufException.negativeSize(); } // save old limit final int oldLimit = this.limit; this.limit = offset + length; if (value == null) { value = schema.newMessage(); } schema.mergeFrom(this, value); checkLastTagWas(0); // restore old limit this.limit = oldLimit; return value; } /** * Reads a var int 32 from the internal byte buffer. */ public int readRawVarint32() throws IOException { byte tmp = buffer[offset++]; if (tmp >= 0) { return tmp; } int result = tmp & 0x7f; if ((tmp = buffer[offset++]) >= 0) { result |= tmp << 7; } else { result |= (tmp & 0x7f) << 7; if ((tmp = buffer[offset++]) >= 0) { result |= tmp << 14; } else { result |= (tmp & 0x7f) << 14; if ((tmp = buffer[offset++]) >= 0) { result |= tmp << 21; } else { result |= (tmp & 0x7f) << 21; result |= (tmp = buffer[offset++]) << 28; if (tmp < 0) { // Discard upper 32 bits. for (int i = 0; i < 5; i++) { if (buffer[offset++] >= 0) { return result; } } throw ProtobufException.malformedVarint(); } } } } return result; } /** * Reads a var int 64 from the internal byte buffer. */ public long readRawVarint64() throws IOException { final byte[] buffer = this.buffer; int offset = this.offset; int shift = 0; long result = 0; while (shift < 64) { final byte b = buffer[offset++]; result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { this.offset = offset; return result; } shift += 7; } throw ProtobufException.malformedVarint(); } /** * Read a 32-bit little-endian integer from the internal buffer. */ public int readRawLittleEndian32() { final byte[] buffer = this.buffer; int offset = this.offset; final byte b1 = buffer[offset++]; final byte b2 = buffer[offset++]; final byte b3 = buffer[offset++]; final byte b4 = buffer[offset++]; this.offset = offset; return (((int) b1 & 0xff)) | (((int) b2 & 0xff) << 8) | (((int) b3 & 0xff) << 16) | (((int) b4 & 0xff) << 24); } /** * Read a 64-bit little-endian integer from the internal byte buffer. */ public long readRawLittleEndian64() { final byte[] buffer = this.buffer; int offset = this.offset; final byte b1 = buffer[offset++]; final byte b2 = buffer[offset++]; final byte b3 = buffer[offset++]; final byte b4 = buffer[offset++]; final byte b5 = buffer[offset++]; final byte b6 = buffer[offset++]; final byte b7 = buffer[offset++]; final byte b8 = buffer[offset++]; this.offset = offset; return (((long) b1 & 0xff)) | (((long) b2 & 0xff) << 8) | (((long) b3 & 0xff) << 16) | (((long) b4 & 0xff) << 24) | (((long) b5 & 0xff) << 32) | (((long) b6 & 0xff) << 40) | (((long) b7 & 0xff) << 48) | (((long) b8 & 0xff) << 56); } /** * Reads a byte array/ByteBuffer value. */ @Override public ByteBuffer readByteBuffer() throws IOException { return ByteBuffer.wrap(readByteArray()); } @Override public ArrayBuilders getArrayBuilders() { if (arrayBuilders == null) { arrayBuilders = new ArrayBuilders(); } return arrayBuilders; } @Override public int readPackedInt32() throws IOException { checkIfPackedField(); return readRawVarint32(); } @Override public int readPackedUInt32() throws IOException { checkIfPackedField(); return readRawVarint32(); } @Override public int readPackedSInt32() throws IOException { checkIfPackedField(); final int n = readRawVarint32(); return (n >>> 1) ^ -(n & 1); } @Override public int readPackedFixed32() throws IOException { checkIfPackedField(); return readRawLittleEndian32(); } @Override public int readPackedSFixed32() throws IOException { checkIfPackedField(); return readRawLittleEndian32(); } @Override public long readPackedInt64() throws IOException { checkIfPackedField(); return readRawVarint64(); } @Override public long readPackedUInt64() throws IOException { checkIfPackedField(); return readRawVarint64(); } @Override public long readPackedSInt64() throws IOException { checkIfPackedField(); final long n = readRawVarint64(); return (n >>> 1) ^ -(n & 1); } @Override public long readPackedFixed64() throws IOException { checkIfPackedField(); return readRawLittleEndian64(); } @Override public long readPackedSFixed64() throws IOException { checkIfPackedField(); return readRawLittleEndian64(); } @Override public float readPackedFloat() throws IOException { checkIfPackedField(); return Float.intBitsToFloat(readRawLittleEndian32()); } @Override public double readPackedDouble() throws IOException { checkIfPackedField(); return Double.longBitsToDouble(readRawLittleEndian64()); } @Override public boolean readPackedBool() throws IOException { checkIfPackedField(); return buffer[offset++] != 0; } @Override public int readPackedEnum() throws IOException { checkIfPackedField(); return readRawVarint32(); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java ================================================ //======================================================================== //Copyright 2007-2009 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; import java.io.IOException; import java.nio.ByteBuffer; import com.fasterxml.jackson.databind.util.ArrayBuilders; /** * Forked and modified from protostuff
* * An Input lets an application read primitive data types and objects from a source of data. * * @author David Yu * @created Nov 9, 2009 */ public interface InputEx { /** * The underlying implementation should handle the unknown field. */ void handleUnknownField(int fieldNumber) throws IOException; /** * Reads field number. */ int readFieldNumber() throws IOException; /** * Reads a variable int field value. */ int readInt32() throws IOException; /** * Reads an unsigned int field value. */ int readUInt32() throws IOException; /** * Reads a signed int field value. */ int readSInt32() throws IOException; /** * Reads a fixed int(4 bytes) field value. */ int readFixed32() throws IOException; /** * Reads a signed+fixed int(4 bytes) field value. */ int readSFixed32() throws IOException; /** * Reads a variable long field value. */ long readInt64() throws IOException; /** * Reads an unsigned long field value. */ long readUInt64() throws IOException; /** * Reads a signed long field value. */ long readSInt64() throws IOException; /** * Reads a fixed long(8 bytes) field value. */ long readFixed64() throws IOException; /** * Reads a signed+fixed long(8 bytes) field value. */ long readSFixed64() throws IOException; /** * Reads a float field value. */ float readFloat() throws IOException; /** * Reads a double field value. */ double readDouble() throws IOException; /** * Reads a boolean field value. */ boolean readBool() throws IOException; /** * Reads an enum(its number) field value. */ int readEnum() throws IOException; /** * Reads a {@link String} field value. */ String readString() throws IOException; /** * Reads a {@link ByteString} field value. */ ByteString readBytes() throws IOException; /** * Reads a byte array field value. */ byte[] readByteArray() throws IOException; ByteBuffer readByteBuffer() throws IOException; /** * Merges an object(with schema) field value. The provided {@link Schema schema} handles the deserialization for the * object. */ T mergeObject(T value, SchemaReader schema) throws IOException; ArrayBuilders getArrayBuilders(); int readPackedInt32() throws IOException; int readPackedUInt32() throws IOException; int readPackedSInt32() throws IOException; int readPackedFixed32() throws IOException; int readPackedSFixed32() throws IOException; long readPackedInt64() throws IOException; long readPackedUInt64() throws IOException; long readPackedSInt64() throws IOException; long readPackedFixed64() throws IOException; long readPackedSFixed64() throws IOException; float readPackedFloat() throws IOException; double readPackedDouble() throws IOException; boolean readPackedBool() throws IOException; int readPackedEnum() throws IOException; } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java ================================================ //======================================================================== //Copyright 2007-2009 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; import static io.protostuff.ProtobufOutputEx.encodeZigZag64; import java.io.IOException; import java.nio.ByteBuffer; /** *
 * Forked and modified from protostuff
 * difference for scalar types:
 *                   OutputEx         OutputEx
 * writeXXX          all scenes     repeated not packed, write with tag, not ignore default value
 * writePackedXXX    ---            repeated packed, write without tag, not ignore default value
 * writeScalarXXX    ---            not repeated field, not write default value
 * 
* * An OutputEx lets an application write primitive data types and objects to a sink of data. * * @author David Yu * @created Nov 9, 2009 */ public interface OutputEx { /** * Writes a variable int field. */ void writeInt32(int tag, int tagSize, int value) throws IOException; /** * Writes an unsigned int field. */ void writeUInt32(int tag, int tagSize, int value) throws IOException; /** * Writes a signed int field. */ void writeSInt32(int tag, int tagSize, int value) throws IOException; /** * Writes a fixed int(4 bytes) field. */ void writeFixed32(int tag, int tagSize, int value) throws IOException; /** * Writes a signed+fixed int(4 bytes) field. */ void writeSFixed32(int tag, int tagSize, int value) throws IOException; /** * Writes a variable long field. */ void writeInt64(int tag, int tagSize, long value) throws IOException; /** * Writes an unsigned long field. */ void writeUInt64(int tag, int tagSize, long value) throws IOException; /** * Writes a signed long field. */ void writeSInt64(int tag, int tagSize, long value) throws IOException; /** * Writes a fixed long(8 bytes) field. */ void writeFixed64(int tag, int tagSize, long value) throws IOException; /** * Writes a signed+fixed long(8 bytes) field. */ void writeSFixed64(int tag, int tagSize, long value) throws IOException; /** * Writes a float field. */ void writeFloat(int tag, int tagSize, float value) throws IOException; /** * Writes a double field. */ void writeDouble(int tag, int tagSize, double value) throws IOException; /** * Writes a boolean field. */ void writeBool(int tag, int tagSize, boolean value) throws IOException; /** * Writes a enum(its number) field. */ void writeEnum(int tag, int tagSize, int value) throws IOException; /** * Writes a String field. */ void writeString(int tag, int tagSize, String value) throws IOException; /** * Writes a ByteString(wraps byte array) field. */ void writeBytes(int tag, int tagSize, ByteString value) throws IOException; /** * Writes a byte array field. */ void writeByteArray(int tag, int tagSize, byte[] value) throws IOException; /** * Writes a binary or a pre-encoded utf8 string. */ void writeByteRange(boolean utf8String, int tag, int tagSize, byte[] value, int offset, int length) throws IOException; /** * Writes an object(using its schema) field. */ void writeObject(int tag, int tagSize, T value, SchemaWriter schemaWriter) throws IOException; void writeBytes(int tag, int tagSize, ByteBuffer value) throws IOException; byte[] toByteArray(); void writePackedInt32(int value) throws IOException; void writeScalarInt32(int tag, int tagSize, int value) throws IOException; default void writePackedInt64(long value) throws IOException { writePackedUInt64(value); } void writeScalarInt64(int tag, int tagSize, long value) throws IOException; void writePackedUInt32(int value) throws IOException; void writeScalarUInt32(int tag, int tagSize, int value) throws IOException; void writePackedUInt64(long value) throws IOException; void writeScalarUInt64(int tag, int tagSize, long value) throws IOException; void writePackedSInt32(int value) throws IOException; void writeScalarSInt32(int tag, int tagSize, int value) throws IOException; default void writePackedSInt64(long value) throws IOException { writePackedUInt64(encodeZigZag64(value)); } void writeScalarSInt64(int tag, int tagSize, long value) throws IOException; void writePackedFixed32(int value) throws IOException; void writeScalarFixed32(int tag, int tagSize, int value) throws IOException; void writePackedFixed64(long value) throws IOException; void writeScalarFixed64(int tag, int tagSize, long value) throws IOException; default void writePackedSFixed32(int value) throws IOException { writePackedFixed32(value); } void writeScalarSFixed32(int tag, int tagSize, int value) throws IOException; default void writePackedSFixed64(long value) throws IOException { writePackedFixed64(value); } void writeScalarSFixed64(int tag, int tagSize, long value) throws IOException; default void writePackedFloat(float value) throws IOException { writePackedFixed32(Float.floatToRawIntBits(value)); } void writeScalarFloat(int tag, int tagSize, float value) throws IOException; default void writePackedDouble(double value) throws IOException { writePackedFixed64(Double.doubleToRawLongBits(value)); } void writeScalarDouble(int tag, int tagSize, double value) throws IOException; void writePackedBool(boolean value) throws IOException; void writeScalarBool(int tag, int tagSize, boolean value) throws IOException; default void writePackedEnum(int value) throws IOException { writePackedInt32(value); } void writeScalarEnum(int tag, int tagSize, int value) throws IOException; void writeScalarString(int tag, int tagSize, String value) throws IOException; } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java ================================================ //======================================================================== //Copyright 2007-2010 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; import static io.protostuff.StringSerializer.writeUTF8VarDelimited; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; /** * Forked and modified from protostuff
* * Protobuf serialization where the messages must be fully buffered on memory before it can be written to the socket ( * {@link OutputStream}). * * @author David Yu * @created May 18, 2010 */ public final class ProtobufOutputEx extends WriteSession implements OutputEx { public static final int LITTLE_ENDIAN_32_SIZE = 4, LITTLE_ENDIAN_64_SIZE = 8; public ProtobufOutputEx() { super(LinkedBuffer.allocate()); } public ProtobufOutputEx(LinkedBuffer buffer) { super(buffer); } public ProtobufOutputEx(LinkedBuffer buffer, int nextBufferSize) { super(buffer, nextBufferSize); } /** * Resets this output for re-use. */ @Override public ProtobufOutputEx clear() { super.clear(); return this; } @Override public void writeInt32(int tag, int tagSize, int value) { if (value < 0) { tail = writeTagAndRawVarInt64(tag, tagSize, value, this, tail); return; } tail = writeTagAndRawVarInt32(tag, tagSize, value, this, tail); } @Override public void writeUInt32(int tag, int tagSize, int value) { tail = writeTagAndRawVarInt32(tag, tagSize, value, this, tail); } @Override public void writeSInt32(int tag, int tagSize, int value) { tail = writeTagAndRawVarInt32(tag, tagSize, encodeZigZag32(value), this, tail); } @Override public void writeFixed32(int tag, int tagSize, int value) { tail = writeTagAndRawLittleEndian32(tag, tagSize, value, this, tail); } @Override public void writeSFixed32(int tag, int tagSize, int value) { tail = writeTagAndRawLittleEndian32(tag, tagSize, value, this, tail); } @Override public void writeInt64(int tag, int tagSize, long value) { tail = writeTagAndRawVarInt64(tag, tagSize, value, this, tail); } @Override public void writeUInt64(int tag, int tagSize, long value) { tail = writeTagAndRawVarInt64(tag, tagSize, value, this, tail); } @Override public void writeSInt64(int tag, int tagSize, long value) { tail = writeTagAndRawVarInt64(tag, tagSize, encodeZigZag64(value), this, tail); } @Override public void writeFixed64(int tag, int tagSize, long value) { tail = writeTagAndRawLittleEndian64(tag, tagSize, value, this, tail); } @Override public void writeSFixed64(int tag, int tagSize, long value) { tail = writeTagAndRawLittleEndian64(tag, tagSize, value, this, tail); } @Override public void writeFloat(int tag, int tagSize, float value) { tail = writeTagAndRawLittleEndian32(tag, tagSize, Float.floatToRawIntBits(value), this, tail); } @Override public void writeDouble(int tag, int tagSize, double value) { tail = writeTagAndRawLittleEndian64(tag, tagSize, Double.doubleToRawLongBits(value), this, tail); } @Override public void writeBool(int tag, int tagSize, boolean value) { tail = writeTagAndRawVarInt32(tag, tagSize, value ? 1 : 0, this, tail); } @Override public void writeEnum(int tag, int tagSize, int number) { writeInt32(tag, tagSize, number); } @Override public void writeString(int tag, int tagSize, String value) { tail = writeUTF8VarDelimited( value, this, writeRawVarInt32(tag, this, tail)); } @Override public void writeBytes(int tag, int tagSize, ByteString value) { writeByteArray(tag, tagSize, value.getBytes()); } @Override public void writeByteArray(int tag, int tagSize, byte[] bytes) { tail = writeTagAndByteArray( tag, tagSize, bytes, 0, bytes.length, this, tail); } @Override public void writeByteRange(boolean utf8String, int tag, int tagSize, byte[] value, int offset, int length) { tail = writeTagAndByteArray( tag, tagSize, value, offset, length, this, tail); } @Override public void writeObject(final int tag, int tagSize, final T value, final SchemaWriter schemaWriter) throws IOException { final LinkedBuffer lastBuffer; // write the tag if (tagSize == 1 && tail.offset != tail.buffer.length) { lastBuffer = tail; size++; lastBuffer.buffer[lastBuffer.offset++] = (byte) tag; } else { tail = lastBuffer = writeRawVarInt32(tag, this, tail); } final int lastOffset = tail.offset, lastSize = size; if (lastOffset == lastBuffer.buffer.length) { // not enough size for the 1-byte delimiter final LinkedBuffer nextBuffer = new LinkedBuffer(nextBufferSize); // new buffer for the content tail = nextBuffer; schemaWriter.writeTo(this, value); final int msgSize = size - lastSize; final byte[] delimited = new byte[computeRawVarint32Size(msgSize)]; writeRawVarInt32(msgSize, delimited, 0); size += delimited.length; // wrap the byte array (delimited) and insert between the two buffers new LinkedBuffer(delimited, 0, delimited.length, lastBuffer).next = nextBuffer; return; } // we have enough space for the 1-byte delim lastBuffer.offset++; size++; schemaWriter.writeTo(this, value); final int msgSize = size - lastSize - 1; // optimize for small messages if (msgSize < 128) { // fits lastBuffer.buffer[lastOffset] = (byte) msgSize; return; } // split into two buffers // the second buffer (contains the message contents) final LinkedBuffer view = new LinkedBuffer(lastBuffer.buffer, lastOffset + 1, lastBuffer.offset); if (lastBuffer == tail) { tail = view; } else { view.next = lastBuffer.next; } // the first buffer (contains the tag) lastBuffer.offset = lastOffset; final byte[] delimited = new byte[computeRawVarint32Size(msgSize)]; writeRawVarInt32(msgSize, delimited, 0); // add the difference size += (delimited.length - 1); // wrap the byte array (delimited) and insert between the two buffers new LinkedBuffer(delimited, 0, delimited.length, lastBuffer).next = view; } /* * Write the nested message encoded as group. * * void writeObjectEncodedAsGroup(final int fieldNumber, final T value, final SchemaEx schema, final boolean * repeated) throws IOException { tail = writeRawVarInt32( makeTag(fieldNumber, WIRETYPE_START_GROUP), this, tail); * * schema.writeTo(this, value); * * tail = writeRawVarInt32( makeTag(fieldNumber, WIRETYPE_END_GROUP), this, tail); } */ /* ----------------------------------------------------------------- */ /** * Returns the buffer encoded with the variable int 32. */ public static LinkedBuffer writeRawVarInt32(int value, final WriteSession session, LinkedBuffer lb) { final int size = computeRawVarint32Size(value); if (lb.offset + size > lb.buffer.length) { lb = new LinkedBuffer(session.nextBufferSize, lb); } final byte[] buffer = lb.buffer; int offset = lb.offset; lb.offset += size; session.size += size; if (size == 1) { buffer[offset] = (byte) value; } else { for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { buffer[offset++] = (byte) ((value & 0x7F) | 0x80); } buffer[offset] = (byte) value; } return lb; } /** * Returns the buffer encoded with the tag and byte array */ public static LinkedBuffer writeTagAndByteArray(int tag, int tagSize, final byte[] value, int offset, int valueLen, final WriteSession session, LinkedBuffer lb) { if (valueLen == 0) { // write only the tag and delimiter return writeTagAndRawVarInt32(tag, tagSize, valueLen, session, lb); } lb = writeTagAndRawVarInt32(tag, tagSize, valueLen, session, lb); session.size += valueLen; final int available = lb.buffer.length - lb.offset; if (valueLen > available) { if (available + session.nextBufferSize < valueLen) { // too large ... so we wrap and insert (zero-copy) if (available == 0) { // buffer was actually full ... return a fresh buffer return new LinkedBuffer(session.nextBufferSize, new LinkedBuffer(value, offset, offset + valueLen, lb)); } // continue with the existing byte array of the previous buffer return new LinkedBuffer(lb, new LinkedBuffer(value, offset, offset + valueLen, lb)); } // copy what can fit System.arraycopy(value, offset, lb.buffer, lb.offset, available); lb.offset += available; // grow lb = new LinkedBuffer(session.nextBufferSize, lb); final int leftover = valueLen - available; // copy what's left System.arraycopy(value, offset + available, lb.buffer, 0, leftover); lb.offset += leftover; return lb; } // it fits System.arraycopy(value, offset, lb.buffer, lb.offset, valueLen); lb.offset += valueLen; return lb; } /** * Returns the buffer encoded with the tag and var int 32 */ public static LinkedBuffer writeTagAndRawVarInt32(int tag, int tagSize, int value, final WriteSession session, LinkedBuffer lb) { final int size = computeRawVarint32Size(value); final int totalSize = tagSize + size; if (lb.offset + totalSize > lb.buffer.length) { lb = new LinkedBuffer(session.nextBufferSize, lb); } final byte[] buffer = lb.buffer; int offset = lb.offset; lb.offset += totalSize; session.size += totalSize; if (tagSize == 1) { buffer[offset++] = (byte) tag; } else { for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); } buffer[offset++] = (byte) tag; } if (size == 1) { buffer[offset] = (byte) value; } else { for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { buffer[offset++] = (byte) ((value & 0x7F) | 0x80); } buffer[offset] = (byte) value; } return lb; } /** * Returns the buffer encoded with the tag and var int 64 */ public static LinkedBuffer writeTagAndRawVarInt64(int tag, int tagSize, long value, final WriteSession session, LinkedBuffer lb) { final int size = computeRawVarint64Size(value); final int totalSize = tagSize + size; if (lb.offset + totalSize > lb.buffer.length) { lb = new LinkedBuffer(session.nextBufferSize, lb); } final byte[] buffer = lb.buffer; int offset = lb.offset; lb.offset += totalSize; session.size += totalSize; if (tagSize == 1) { buffer[offset++] = (byte) tag; } else { for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); } buffer[offset++] = (byte) tag; } if (size == 1) { buffer[offset] = (byte) value; } else { for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { buffer[offset++] = (byte) (((int) value & 0x7F) | 0x80); } buffer[offset] = (byte) value; } return lb; } /** * Returns the buffer encoded with the tag and little endian 32 */ public static LinkedBuffer writeTagAndRawLittleEndian32(int tag, int tagSize, int value, final WriteSession session, LinkedBuffer lb) { final int totalSize = tagSize + LITTLE_ENDIAN_32_SIZE; if (lb.offset + totalSize > lb.buffer.length) { lb = new LinkedBuffer(session.nextBufferSize, lb); } final byte[] buffer = lb.buffer; int offset = lb.offset; lb.offset += totalSize; session.size += totalSize; if (tagSize == 1) { buffer[offset++] = (byte) tag; } else { for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); } buffer[offset++] = (byte) tag; } writeRawLittleEndian32(value, buffer, offset); return lb; } /** * Returns the buffer encoded with the tag and little endian 64 */ public static LinkedBuffer writeTagAndRawLittleEndian64(int tag, int tagSize, long value, final WriteSession session, LinkedBuffer lb) { final int totalSize = tagSize + LITTLE_ENDIAN_64_SIZE; if (lb.offset + totalSize > lb.buffer.length) { lb = new LinkedBuffer(session.nextBufferSize, lb); } final byte[] buffer = lb.buffer; int offset = lb.offset; lb.offset += totalSize; session.size += totalSize; if (tagSize == 1) { buffer[offset++] = (byte) tag; } else { for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); } buffer[offset++] = (byte) tag; } writeRawLittleEndian64(value, buffer, offset); return lb; } /** Encode and write a varint to the byte array */ public static void writeRawVarInt32(int value, final byte[] buf, int offset) throws IOException { while (true) { if ((value & ~0x7F) == 0) { buf[offset] = (byte) value; return; } else { buf[offset++] = (byte) ((value & 0x7F) | 0x80); value >>>= 7; } } } /** * Writes the encoded little endian 32 and returns the bytes written */ public static int writeRawLittleEndian32(int value, byte[] buffer, int offset) { if (buffer.length - offset < LITTLE_ENDIAN_32_SIZE) { throw new IllegalArgumentException("buffer capacity not enough."); } buffer[offset++] = (byte) (value & 0xFF); buffer[offset++] = (byte) (value >> 8 & 0xFF); buffer[offset++] = (byte) (value >> 16 & 0xFF); buffer[offset] = (byte) (value >> 24 & 0xFF); return LITTLE_ENDIAN_32_SIZE; } /** * Writes the encoded little endian 64 and returns the bytes written */ public static int writeRawLittleEndian64(long value, byte[] buffer, int offset) { if (buffer.length - offset < LITTLE_ENDIAN_64_SIZE) { throw new IllegalArgumentException("buffer capacity not enough."); } buffer[offset++] = (byte) (value & 0xFF); buffer[offset++] = (byte) (value >> 8 & 0xFF); buffer[offset++] = (byte) (value >> 16 & 0xFF); buffer[offset++] = (byte) (value >> 24 & 0xFF); buffer[offset++] = (byte) (value >> 32 & 0xFF); buffer[offset++] = (byte) (value >> 40 & 0xFF); buffer[offset++] = (byte) (value >> 48 & 0xFF); buffer[offset] = (byte) (value >> 56 & 0xFF); return LITTLE_ENDIAN_64_SIZE; } /* METHODS FROM CodedOutput */ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** * Compute the number of bytes that would be needed to encode a varint. {@code value} is treated as unsigned, so it * won't be sign-extended if negative. */ public static int computeRawVarint32Size(final int value) { if ((value & (0xffffffff << 7)) == 0) { return 1; } if ((value & (0xffffffff << 14)) == 0) { return 2; } if ((value & (0xffffffff << 21)) == 0) { return 3; } if ((value & (0xffffffff << 28)) == 0) { return 4; } return 5; } /** * Compute the number of bytes that would be needed to encode a varint. */ public static int computeRawVarint64Size(final long value) { if ((value & (0xffffffffffffffffL << 7)) == 0) { return 1; } if ((value & (0xffffffffffffffffL << 14)) == 0) { return 2; } if ((value & (0xffffffffffffffffL << 21)) == 0) { return 3; } if ((value & (0xffffffffffffffffL << 28)) == 0) { return 4; } if ((value & (0xffffffffffffffffL << 35)) == 0) { return 5; } if ((value & (0xffffffffffffffffL << 42)) == 0) { return 6; } if ((value & (0xffffffffffffffffL << 49)) == 0) { return 7; } if ((value & (0xffffffffffffffffL << 56)) == 0) { return 8; } if ((value & (0xffffffffffffffffL << 63)) == 0) { return 9; } return 10; } /** * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers into values that can be efficiently encoded * with varint. (Otherwise, negative values must be sign-extended to 64 bits to be varint encoded, thus always * taking 10 bytes on the wire.) * * @param n * A signed 32-bit integer. * @return An unsigned 32-bit integer, stored in a signed int because Java has no explicit unsigned support. */ public static int encodeZigZag32(final int n) { // Note: the right-shift must be arithmetic return (n << 1) ^ (n >> 31); } /** * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be efficiently encoded * with varint. (Otherwise, negative values must be sign-extended to 64 bits to be varint encoded, thus always * taking 10 bytes on the wire.) * * @param n * A signed 64-bit integer. * @return An unsigned 64-bit integer, stored in a signed int because Java has no explicit unsigned support. */ public static long encodeZigZag64(final long n) { // Note: the right-shift must be arithmetic return (n << 1) ^ (n >> 63); } /** * Writes a ByteBuffer field. */ @Override public void writeBytes(int tag, int tagSize, ByteBuffer value) { writeByteRange(false, tag, tagSize, value.array(), value.arrayOffset() + value.position(), value.remaining()); } public void toOutputStream(OutputStream outputStream) throws IOException { LinkedBuffer.writeTo(outputStream, head); } @Override public void writePackedInt32(int value) { if (value >= 0) { tail = writeRawVarInt32(value, this, tail); return; } writePackedUInt64(value); } @Override public void writeScalarInt32(int tag, int tagSize, int value) { if (value != 0) { writeInt32(tag, tagSize, value); } } @Override public void writeScalarInt64(int tag, int tagSize, long value) { if (value != 0) { writeInt64(tag, tagSize, value); } } @Override public void writePackedUInt32(int value) { tail = writeRawVarInt32(value, this, tail); } @Override public void writeScalarUInt32(int tag, int tagSize, int value) { if (value != 0) { writeUInt32(tag, tagSize, value); } } @Override public void writePackedUInt64(long value) { final int size = computeRawVarint64Size(value); if (tail.offset + size > tail.buffer.length) { tail = new LinkedBuffer(this.nextBufferSize, tail); } final byte[] buffer = tail.buffer; int offset = tail.offset; tail.offset += size; this.size += size; if (size == 1) { buffer[offset] = (byte) value; } else { for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { buffer[offset++] = (byte) (((int) value & 0x7F) | 0x80); } buffer[offset] = (byte) value; } } @Override public void writeScalarUInt64(int tag, int tagSize, long value) { if (value != 0) { writeUInt64(tag, tagSize, value); } } @Override public void writePackedSInt32(int value) { tail = writeRawVarInt32(encodeZigZag32(value), this, tail); } @Override public void writeScalarSInt32(int tag, int tagSize, int value) { if (value != 0) { writeSInt32(tag, tagSize, value); } } @Override public void writeScalarSInt64(int tag, int tagSize, long value) { if (value != 0) { writeSInt64(tag, tagSize, value); } } @Override public void writePackedFixed32(int value) { final int size = LITTLE_ENDIAN_32_SIZE; if (tail.offset + size > tail.buffer.length) { tail = new LinkedBuffer(this.nextBufferSize, tail); } final byte[] buffer = tail.buffer; int offset = tail.offset; tail.offset += size; this.size += size; buffer[offset++] = (byte) (value & 0xFF); buffer[offset++] = (byte) (value >> 8 & 0xFF); buffer[offset++] = (byte) (value >> 16 & 0xFF); buffer[offset] = (byte) (value >> 24 & 0xFF); } @Override public void writeScalarFixed32(int tag, int tagSize, int value) { if (value != 0) { writeFixed32(tag, tagSize, value); } } @Override public void writePackedFixed64(long value) { final int size = LITTLE_ENDIAN_64_SIZE; if (tail.offset + size > tail.buffer.length) { tail = new LinkedBuffer(this.nextBufferSize, tail); } final byte[] buffer = tail.buffer; int offset = tail.offset; tail.offset += size; this.size += size; buffer[offset++] = (byte) (value & 0xFF); buffer[offset++] = (byte) (value >> 8 & 0xFF); buffer[offset++] = (byte) (value >> 16 & 0xFF); buffer[offset++] = (byte) (value >> 24 & 0xFF); buffer[offset++] = (byte) (value >> 32 & 0xFF); buffer[offset++] = (byte) (value >> 40 & 0xFF); buffer[offset++] = (byte) (value >> 48 & 0xFF); buffer[offset] = (byte) (value >> 56 & 0xFF); } @Override public void writeScalarFixed64(int tag, int tagSize, long value) { if (value != 0) { writeFixed64(tag, tagSize, value); } } @Override public void writeScalarSFixed32(int tag, int tagSize, int value) { if (value != 0) { writeSFixed32(tag, tagSize, value); } } @Override public void writeScalarSFixed64(int tag, int tagSize, long value) { if (value != 0) { writeSFixed64(tag, tagSize, value); } } @Override public void writeScalarFloat(int tag, int tagSize, float value) { if (value != 0) { writeFloat(tag, tagSize, value); } } @Override public void writeScalarDouble(int tag, int tagSize, double value) { if (value != 0) { writeDouble(tag, tagSize, value); } } @Override public void writePackedBool(boolean value) { final int size = 1; if (tail.offset + size > tail.buffer.length) { tail = new LinkedBuffer(this.nextBufferSize, tail); } final byte[] buffer = tail.buffer; int offset = tail.offset; tail.offset += size; this.size += size; buffer[offset] = (byte) (value ? 1 : 0); } @Override public void writeScalarBool(int tag, int tagSize, boolean value) { if (value) { writeBool(tag, tagSize, value); } } @Override public void writeScalarEnum(int tag, int tagSize, int value) { writeScalarInt32(tag, tagSize, value); } public void writeScalarString(int tag, int tagSize, String value) { writeString(tag, tagSize, value); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java ================================================ //======================================================================== //Copyright 2007-2009 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; /** * Forked and modified from protostuff * * Handles the serialization and deserialization of a message/object tied to this. *

* Basically, any object can be serialized via protobuf. As long as its schema is provided, it does not need to * implement {@link Message}. This was designed with "unobtrusive" in mind. The goal was to be able to * serialize/deserialize any existing object without having to touch its source. This will enable you to customize the * serialization of objects from 3rd party libraries. * * @author David Yu * @created Nov 9, 2009 */ public interface SchemaEx extends SchemaWriter, SchemaReader { /** * Returns the simple name of the message tied to this schema. Allows custom schemas to provide a custom name other * than typeClass().getSimpleName(); */ default String messageName() { throw new UnsupportedOperationException(); } void init(); } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java ================================================ //======================================================================== //Copyright 2007-2009 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; import java.io.IOException; public interface SchemaReader { /** * Creates the message/object tied to this schema. */ default T newMessage() { return null; } void mergeFrom(InputEx input, T message) throws IOException; } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java ================================================ //======================================================================== //Copyright 2007-2009 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff; import java.io.IOException; public interface SchemaWriter { void writeTo(OutputEx output, T value) throws IOException; } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** *

 * classes in protostuff are "final" or not all suitable for ServiceComb
 *   1.final
 *     io.protostuff.ProtobufOutputEx
 *     io.protostuff.ByteArrayInput
 *   2.not suitable
 *     1) io.protostuff.OutputEx
 *        fieldNumber changed to tag, avoid makeTag every time
 *        not support write packed value
 *        not support ignore default scalar value
 *     2) io.protostuff.SchemaEx
 *        make it simpler
 *     3) codec of map is not compatible to protobuf
 * so we must copy and modified them
 * 
*/ package io.protostuff; ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java ================================================ //======================================================================== //Copyright 2007-2010 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff.runtime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Forked and modified from protostuff * * Field mapping implemented on top of java array for lookup by number. * * This is the most efficient implementation for almost all cases. But * it should not be used when field numbers are sparse and especially * when max field number is big - as this mapping internally uses array * of integers with size equal to max field number. In latter case * {@code HashFieldMapEx} should be used. * * @see HashFieldMapEx * * @author Kostiantyn Shchepanovskyi */ public final class ArrayFieldMapEx implements FieldMapEx { private final List> fields; private final FieldSchema[] fieldsByNumber; private final Map> fieldsByName; @SuppressWarnings("unchecked") public ArrayFieldMapEx(Collection> fields, int lastFieldNumber) { fieldsByName = new HashMap<>(); fieldsByNumber = (FieldSchema[]) new FieldSchema[lastFieldNumber + 1]; for (FieldSchema f : fields) { FieldSchema last = this.fieldsByName.put(f.name, f); if (last != null) { throw new IllegalStateException(last + " and " + f + " cannot have the same name."); } if (fieldsByNumber[f.getFieldNumber()] != null) { throw new IllegalStateException( fieldsByNumber[f.getFieldNumber()] + " and " + f + " cannot have the same number."); } fieldsByNumber[f.getFieldNumber()] = f; } List> fieldList = new ArrayList<>(fields.size()); for (FieldSchema field : fieldsByNumber) { if (field != null) { fieldList.add(field); } } this.fields = Collections.unmodifiableList(fieldList); } @Override public FieldSchema getFieldByNumber(int n) { return n < fieldsByNumber.length ? fieldsByNumber[n] : null; } @Override public FieldSchema getFieldByName(String fieldName) { return fieldsByName.get(fieldName); } /** * Returns the message's total number of fields. */ @Override public int getFieldCount() { return fields.size(); } @Override public List> getFields() { return fields; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java ================================================ //======================================================================== //Copyright 2007-2010 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff.runtime; import java.util.Collection; import java.util.List; /** * Forked and modified from protostuff * * Interface for map of fields - defines how to you get field by name or number (tag). * * @author Kostiantyn Shchepanovskyi */ public interface FieldMapEx { int MIN_TAG_FOR_HASH_FIELD_MAP = 100; static boolean preferHashFieldMap(int fieldCount, int lastFieldNumber) { return lastFieldNumber > MIN_TAG_FOR_HASH_FIELD_MAP && lastFieldNumber >= 2 * fieldCount; } static FieldMapEx createFieldMap(Collection> fields) { int lastFieldNumber = 0; for (FieldSchema field : fields) { if (field.getFieldNumber() > lastFieldNumber) { lastFieldNumber = field.getFieldNumber(); } } if (preferHashFieldMap(fields.size(), lastFieldNumber)) { return new HashFieldMapEx<>(fields); } // array field map should be more efficient return new ArrayFieldMapEx<>(fields, lastFieldNumber); } FieldSchema getFieldByNumber(int n); FieldSchema getFieldByName(String fieldName); int getFieldCount(); List> getFields(); } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java ================================================ //======================================================================== //Copyright 2007-2009 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff.runtime; import java.io.IOException; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.ProtobufOutputEx; import io.protostuff.WireFormat; import io.protostuff.compiler.model.Field; /** * Forked and modified from protostuff * * Represents a field of a message/pojo. */ public abstract class FieldSchema { protected final Field protoField; protected final String name; protected final int fieldNumber; protected final int tag; protected final int tagSize; protected final boolean packed; protected final JavaType javaType; protected final boolean primitive; public FieldSchema(Field protoField, JavaType javaType) { this.protoField = protoField; this.name = protoField.getName(); this.fieldNumber = protoField.getTag(); this.packed = ProtoUtils.isPacked(protoField); int wireType = packed && protoField.isRepeated() ? WireFormat.WIRETYPE_LENGTH_DELIMITED : FieldTypeUtils.convert(protoField.getType()).wireType; this.tag = WireFormat.makeTag(fieldNumber, wireType); this.tagSize = ProtobufOutputEx.computeRawVarint32Size(tag); this.javaType = javaType; this.primitive = javaType.isPrimitive(); } public int getFieldNumber() { return fieldNumber; } public Field getProtoField() { return protoField; } public int mergeFrom(InputEx input, T message) throws IOException { throw new UnsupportedOperationException(); } public boolean isPrimitive() { return primitive; } public void getAndWriteTo(OutputEx output, T message) throws IOException { throw new UnsupportedOperationException(); } /** * write from map * * @param output * @param value field value, will not be null * @throws IOException */ public void writeTo(OutputEx output, Object value) throws IOException { throw new UnsupportedOperationException(); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.protostuff.runtime; import io.protostuff.WireFormat.FieldType; import io.protostuff.compiler.model.ScalarFieldType; public final class FieldTypeUtils { public static FieldType convert(io.protostuff.compiler.model.FieldType fieldType) { if (fieldType.isEnum()) { return FieldType.ENUM; } if (fieldType.isScalar()) { switch ((ScalarFieldType) fieldType) { case INT32: return FieldType.INT32; case INT64: return FieldType.INT64; case UINT32: return FieldType.UINT32; case UINT64: return FieldType.UINT64; case SINT32: return FieldType.SINT32; case SINT64: return FieldType.SINT64; case FIXED32: return FieldType.FIXED32; case FIXED64: return FieldType.FIXED64; case SFIXED32: return FieldType.SFIXED32; case SFIXED64: return FieldType.SFIXED64; case FLOAT: return FieldType.FLOAT; case DOUBLE: return FieldType.DOUBLE; case BOOL: return FieldType.BOOL; case STRING: return FieldType.STRING; case BYTES: return FieldType.BYTES; default: throw new IllegalStateException("bug: miss process of " + fieldType); } } if (fieldType.isMessage()) { return FieldType.MESSAGE; } throw new IllegalStateException("bug: miss process of " + fieldType); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java ================================================ //======================================================================== //Copyright 2007-2010 David Yu dyuproject@gmail.com //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package io.protostuff.runtime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Forked and modified from protostuff * * Field mapping implemented on top of hash for field lookup by number. * * This is the less efficient than {@code ArrayFieldMapEx} for almost all cases. * But in case when field numbers are sparse and especially when max field * number is big - this mapping should be used. * * @see ArrayFieldMapEx * * @author Kostiantyn Shchepanovskyi */ public final class HashFieldMapEx implements FieldMapEx { private final List> fields; private final Map> fieldsByNumber; private final Map> fieldsByName; public HashFieldMapEx(Collection> fields) { fieldsByName = new HashMap<>(); fieldsByNumber = new HashMap<>(); for (FieldSchema f : fields) { if (fieldsByName.containsKey(f.name)) { FieldSchema prev = fieldsByName.get(f.name); throw new IllegalStateException(prev + " and " + f + " cannot have the same name."); } if (fieldsByNumber.containsKey(f.fieldNumber)) { FieldSchema prev = fieldsByNumber.get(f.fieldNumber); throw new IllegalStateException(prev + " and " + f + " cannot have the same number."); } this.fieldsByNumber.put(f.fieldNumber, f); this.fieldsByName.put(f.name, f); } List> fieldList = new ArrayList<>(fields.size()); fieldList.addAll(fields); fieldList.sort(Comparator.comparingInt(FieldSchema::getFieldNumber)); this.fields = Collections.unmodifiableList(fieldList); } @Override public FieldSchema getFieldByNumber(int n) { return fieldsByNumber.get(n); } @Override public FieldSchema getFieldByName(String fieldName) { return fieldsByName.get(fieldName); } @Override public int getFieldCount() { return fields.size(); } @Override public List> getFields() { return fields; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptorManager; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.DeserializerSchemaManager; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.SerializerSchemaManager; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.Proto; import io.protostuff.compiler.model.Service; import io.protostuff.compiler.model.ServiceMethod; public class ProtoMapper { private final ObjectMapper jsonMapper; private final BeanDescriptorManager beanDescriptorManager; private final Proto proto; private final SerializerSchemaManager serializerSchemaManager; private final DeserializerSchemaManager deserializerSchemaManager; // key is message canonical name // this allowed developer to control any type deserializer // otherwise any type will be deserialized to LinkedHashMap private final Map anyTypes = new ConcurrentHashMapEx<>(); protected ProtoMapper(ObjectMapper jsonMapper, BeanDescriptorManager beanDescriptorManager, Proto proto) { this.jsonMapper = jsonMapper; this.beanDescriptorManager = beanDescriptorManager; this.proto = proto; serializerSchemaManager = new SerializerSchemaManager(this); deserializerSchemaManager = new DeserializerSchemaManager(this); } public Proto getProto() { return proto; } public SerializerSchemaManager getSerializerSchemaManager() { return serializerSchemaManager; } public DeserializerSchemaManager getDeserializerSchemaManager() { return deserializerSchemaManager; } public ObjectMapper getJsonMapper() { return jsonMapper; } public BeanDescriptorManager getBeanDescriptorManager() { return beanDescriptorManager; } public Map getAnyTypes() { return anyTypes; } public void addAnyType(String canonicalName, Type type) { anyTypes.put(canonicalName, TypeFactory.defaultInstance().constructType(type)); } public Message getMessageFromCanonicaName(String messageCanonicalName) { for (Message message : proto.getMessages()) { if (message.getCanonicalName().equals(messageCanonicalName)) { return message; } } return null; } public Message getRequestMessage(String operationId) { Service service = proto.getServices().get(0); ServiceMethod serviceMethod = service.getMethod(operationId); if (serviceMethod == null) { throw new IllegalArgumentException("operation not found, operation id=" + operationId); } return serviceMethod.getArgType(); } public Message getResponseMessage(String operationId) { Service service = proto.getServices().get(0); ServiceMethod serviceMethod = service.getMethod(operationId); return serviceMethod.getReturnType(); } public synchronized RootSerializer createRootSerializer(String shortMessageName, Type type) { Message message = proto.getMessage(shortMessageName); if (message == null) { throw new IllegalStateException("can not find proto message to create serializer, name=" + shortMessageName); } return createRootSerializer(message, type); } public synchronized RootSerializer createRootSerializer(Message message, Type type) { return serializerSchemaManager.createRootSerializer(message, type); } public synchronized RootDeserializer createRootDeserializer(String shortMessageName, Type type) { Message message = proto.getMessage(shortMessageName); if (message == null) { throw new IllegalStateException("can not find proto message to create deserializer, name=" + shortMessageName); } return createRootDeserializer(message, type); } public synchronized RootDeserializer createRootDeserializer(Message message, Type type) { return deserializerSchemaManager.createRootDeserializer(message, type); } public synchronized RootDeserializer createRootDeserializer(Message message, Map types) { return deserializerSchemaManager.createRootDeserializer(message, types); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf; import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptorManager; import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import io.protostuff.compiler.model.Proto; public class ProtoMapperFactory { // 1.to support "any" type // 2.to find bean properties private final ObjectMapper jsonMapper = new ObjectMapper(); private final BeanDescriptorManager beanDescriptorManager; private final ProtoParser protoParser = new ProtoParser(); public ProtoMapperFactory() { jsonMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); jsonMapper.setSerializationInclusion(Include.NON_NULL); beanDescriptorManager = new BeanDescriptorManager(jsonMapper.getSerializationConfig()); } public ObjectMapper getJsonMapper() { return jsonMapper; } public BeanDescriptorManager getBeanDescriptorManager() { return beanDescriptorManager; } public ProtoMapper createFromContent(String content) { Proto proto = protoParser.parseFromContent(content); return create(proto); } public ProtoMapper createFromName(String name) { Proto proto = protoParser.parse(name); return create(proto); } public ProtoMapper create(Proto proto) { return new ProtoMapper(jsonMapper, beanDescriptorManager, proto); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootDeserializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf; import java.io.IOException; import io.protostuff.ByteArrayInputEx; import io.protostuff.InputEx; import io.protostuff.SchemaEx; public class RootDeserializer { protected SchemaEx schema; public RootDeserializer(SchemaEx schema) { this.schema = schema; } public SchemaEx getSchema() { return schema; } public void setSchema(SchemaEx schema) { this.schema = schema; } @SuppressWarnings("unchecked") public T deserialize(byte[] bytes) throws IOException { InputEx input = new ByteArrayInputEx(bytes); T instance = schema.newMessage(); schema.mergeFrom(input, instance); return instance; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootSerializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf; import java.io.IOException; import java.io.OutputStream; import io.protostuff.ProtobufOutputEx; import io.protostuff.SchemaEx; public class RootSerializer { private final SchemaEx schema; public RootSerializer(SchemaEx schema) { this.schema = schema; } public byte[] serialize(Object value) throws IOException { ProtobufOutputEx output = new ProtobufOutputEx(); if (value != null) { schema.writeTo(output, value); } return output.toByteArray(); } public void serialize(OutputStream outputStream, Object value) throws IOException { ProtobufOutputEx output = new ProtobufOutputEx(); if (value != null) { schema.writeTo(output, value); } output.toOutputStream(outputStream); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal; import java.util.ArrayList; import java.util.LinkedHashMap; import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.Proto; public final class ProtoConst { private ProtoConst() { } public static final String ANNOTATION_WRAP_ARGUMENTS = "@WrapArguments"; public static final String ANNOTATION_WRAP_PROPERTY = "@WrapProperty"; public static String ANNOTATION_RPC = "@Rpc"; public static final String PACK_SCHEMA = "type.googleapis.com/"; public static final String JSON_SCHEMA = "json/"; public static final String JSON_ID_NAME = "@type"; public static final JavaType MAP_TYPE = TypeFactory.defaultInstance().constructType(LinkedHashMap.class); public static final JavaType LIST_TYPE = TypeFactory.defaultInstance().constructType(ArrayList.class); public static final JavaType OBJECT_TYPE = TypeFactory.defaultInstance().constructType(Object.class); public static final Proto ANY_PROTO; public static final Message ANY; public static final Proto EMPTY_PROTO; public static final Message EMPTY; static { ProtoParser protoParser = new ProtoParser(); ANY_PROTO = protoParser.parse("google/protobuf/any.proto"); ANY = ANY_PROTO.getMessage("Any"); EMPTY_PROTO = protoParser.parse("google/protobuf/empty.proto"); EMPTY = EMPTY_PROTO.getMessage("Empty"); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.compiler.model.DynamicMessage; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.FieldContainer; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.ScalarFieldType; import io.protostuff.compiler.model.Type; public final class ProtoUtils { private ProtoUtils() { } public static boolean isEmptyMessage(Message protoField) { return protoField.getCanonicalName().equals(ProtoConst.EMPTY.getCanonicalName()); } public static boolean isAnyMessage(Message protoField) { return protoField.getCanonicalName().equals(ProtoConst.ANY.getCanonicalName()); } public static boolean isAnyField(Field protoField) { return protoField.getType().getCanonicalName().equals(ProtoConst.ANY.getCanonicalName()); } public static boolean isWrapArguments(FieldContainer fieldContainer) { return fieldContainer.getCommentLines().contains(ProtoConst.ANNOTATION_WRAP_ARGUMENTS); } public static boolean isWrapProperty(FieldContainer fieldContainer) { return fieldContainer.getCommentLines().contains(ProtoConst.ANNOTATION_WRAP_PROPERTY); } /** * all supported type, default to packed * @param protoField * @return */ public static boolean isSupportPacked(Field protoField) { if (protoField.getType().isEnum()) { return true; } if (protoField.getType().isScalar()) { ScalarFieldType scalarFieldType = (ScalarFieldType) protoField.getType(); return scalarFieldType != ScalarFieldType.STRING && scalarFieldType != ScalarFieldType.BYTES; } return false; } public static boolean isPacked(Field protoField) { DynamicMessage.Value dynamicMessageValue = protoField.getOptions().get("packed"); if (dynamicMessageValue != null) { return dynamicMessageValue.getBoolean(); } return isSupportPacked(protoField); } public static void throwNotSupportWrite(Field protoField, Object value) throws IllegalStateException { throwNotSupportWrite(protoField, value.getClass()); } public static void throwNotSupportWrite(Field protoField, Class cls) throws IllegalStateException { throw new IllegalStateException( String.format("not support serialize from %s to proto %s, field=%s:%s", cls.getName(), protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } public static void throwNotSupportMerge(Field protoField, JavaType javaType) throws IllegalStateException { throw new IllegalStateException( String.format("not support deserialize proto %s as %s, field=%s:%s", protoField.getTypeName(), javaType.toCanonical(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } public static void throwNotSupportNullElement(Field protoField) throws IllegalStateException { throw new IllegalStateException( String.format("not support serialize null element, field=%s:%s", ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.bean; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; public class BeanDescriptor { private static final Logger LOGGER = LoggerFactory.getLogger(BeanDescriptor.class); private JavaType javaType; private final Map propertyDescriptors = new HashMap<>(); public JavaType getJavaType() { return javaType; } public Map getPropertyDescriptors() { return propertyDescriptors; } public void init(SerializationConfig serializationConfig, JavaType javaType) { this.javaType = javaType; BeanDescription beanDescription = serializationConfig.introspect(javaType); for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); propertyDescriptor.setName(propertyDefinition.getName()); propertyDescriptor.setJavaType(propertyDefinition.getPrimaryType()); try { propertyDescriptor.setGetter(initGetter(propertyDefinition)); } catch (Throwable e) { LOGGER.error("failed to init getter for field {}:{}", javaType.getRawClass().getName(), propertyDefinition.getName(), e); } try { propertyDescriptor.setSetter(initSetter(propertyDefinition)); } catch (Throwable e) { LOGGER.error("failed to init setter for field {}:{}", javaType.getRawClass().getName(), propertyDefinition.getName(), e); } propertyDescriptors.put(propertyDefinition.getName(), propertyDescriptor); } } @SuppressWarnings("unchecked") public static T initGetter(BeanPropertyDefinition propertyDefinition) { if (propertyDefinition.hasGetter()) { return LambdaMetafactoryUtils.createGetter(propertyDefinition.getGetter().getAnnotated()); } if (propertyDefinition.hasField() && propertyDefinition.getField().isPublic()) { return (T) LambdaMetafactoryUtils.createGetter(propertyDefinition.getField().getAnnotated()); } return null; } protected Object initSetter(BeanPropertyDefinition propertyDefinition) { if (propertyDefinition.hasSetter()) { return LambdaMetafactoryUtils.createSetter(propertyDefinition.getSetter().getAnnotated()); } if (propertyDefinition.hasField() && propertyDefinition.getField().isPublic()) { return LambdaMetafactoryUtils.createSetter(propertyDefinition.getField().getAnnotated()); } return null; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.bean; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.type.TypeFactory; public class BeanDescriptorManager { private final SerializationConfig serializationConfig; private final Map beanDescriptors = new ConcurrentHashMapEx<>(); public BeanDescriptorManager(SerializationConfig serializationConfig) { this.serializationConfig = serializationConfig; } public BeanDescriptor getOrCreateBeanDescriptor(Type type) { return beanDescriptors.computeIfAbsent(type, this::createBeanDescriptor); } protected BeanDescriptor createBeanDescriptor(Type type) { return createBeanDescriptor(TypeFactory.defaultInstance().constructType(type)); } protected BeanDescriptor createBeanDescriptor(JavaType javaType) { BeanDescriptor beanDescriptor = new BeanDescriptor(); beanDescriptor.init(serializationConfig, javaType); return beanDescriptor; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyDescriptor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.bean; import com.fasterxml.jackson.databind.JavaType; public class PropertyDescriptor { private String name; private JavaType javaType; private Object getter; private Object setter; public String getName() { return name; } public void setName(String name) { this.name = name; } public JavaType getJavaType() { return javaType; } public void setJavaType(JavaType javaType) { this.javaType = javaType; } @SuppressWarnings("unchecked") public T getGetter() { return (T) getter; } public void setGetter(Object getter) { this.getter = getter; } @SuppressWarnings("unchecked") public T getSetter() { return (T) setter; } public void setSetter(Object setter) { this.setter = setter; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.bean; public class PropertyWrapper { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.parser; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import io.protostuff.compiler.parser.FileReader; public class ContentFileReader implements FileReader { private final FileReader importReader; private final String content; private boolean contentRead; public ContentFileReader(FileReader importReader, String content) { this.importReader = importReader; this.content = content; } @Override public CharStream read(String contentOrName) { if (!contentRead) { contentRead = true; return CharStreams.fromString(this.content); } return importReader.read(contentOrName); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ProtoParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.parser; import java.util.Collections; import com.google.inject.Guice; import com.google.inject.Injector; import io.protostuff.compiler.ParserModule; import io.protostuff.compiler.model.Proto; import io.protostuff.compiler.parser.FileDescriptorLoader; import io.protostuff.compiler.parser.FileReader; import io.protostuff.compiler.parser.FileReaderFactory; import io.protostuff.compiler.parser.ProtoContext; /** * can be reused */ public class ProtoParser { private static final String DEFAULT_PROTO_NAME = "default.proto"; private final Injector injector = Guice.createInjector(new ParserModule()); private final FileReaderFactory fileReaderFactory = injector.getInstance(FileReaderFactory.class); private final FileReader defaultReader = fileReaderFactory.create(Collections.emptyList()); private final FileDescriptorLoader loader = injector.getInstance(FileDescriptorLoader.class); public Proto parseFromContent(String content) { // io.protostuff.compiler.parser.ClasspathFileReader will use ContextClassLoader load resource, but in some environment, // ContextClassLoader is null, and we use class loader instead. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try { if (classLoader == null) { Thread.currentThread().setContextClassLoader(ProtoParser.class.getClassLoader()); } ProtoContext context = loader.load(new ContentFileReader(defaultReader, content), DEFAULT_PROTO_NAME); return context.getProto(); } finally { Thread.currentThread().setContextClassLoader(classLoader); } } public Proto parse(String name) { // io.protostuff.compiler.parser.ClasspathFileReader will use ContextClassLoader load resource, but in some environment, // ContextClassLoader is null, and we use class loader instead. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try { if (classLoader == null) { Thread.currentThread().setContextClassLoader(ProtoParser.class.getClassLoader()); } ProtoContext context = loader.load(defaultReader, name); return context.getProto(); } finally { Thread.currentThread().setContextClassLoader(classLoader); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/EnumMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.compiler.model.EnumConstant; import io.protostuff.compiler.model.Field; public class EnumMeta { private final Map enumNameToValueMap = new HashMap<>(); // key is idl defined enum value // value is Enum or null private final Map> enumValues = new HashMap<>(); public EnumMeta(Field protoField, JavaType javaType) { io.protostuff.compiler.model.Enum enumType = (io.protostuff.compiler.model.Enum) protoField.getType(); for (EnumConstant enumConstant : enumType.getConstants()) { enumNameToValueMap.put(enumConstant.getName(), enumConstant.getValue()); enumValues.put(enumConstant.getValue(), null); } if (!javaType.isEnumType()) { return; } try { Method method = javaType.getRawClass().getMethod("values"); method.setAccessible(true); Object[] values = (Object[]) method.invoke(null); for (Object value : values) { Enum enumValue = (Enum) value; enumValues.put(enumNameToValueMap.get(enumValue.name()), enumValue); } } catch (Throwable e) { throw new IllegalStateException( "Failed to collect enum values, class=" + javaType.getRawClass().getName(), e); } } public boolean containsValue(Integer value) { return enumValues.containsKey(value); } public Enum getEnumByValue(int enumValue) { return enumValues.get(enumValue); } public Integer getValueByName(String name) { return enumNameToValueMap.get(name); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/MessageAsFieldSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class MessageAsFieldSchema extends FieldSchema { protected final SchemaEx schema; protected final Getter getter; protected final Setter setter; public MessageAsFieldSchema(Field protoField, PropertyDescriptor propertyDescriptor, SchemaEx schema) { super(protoField, propertyDescriptor.getJavaType()); this.schema = schema; this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value == null) { return; } output.writeObject(tag, tagSize, value, schema); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { output.writeObject(tag, tagSize, value, schema); } @Override public final int mergeFrom(InputEx input, T message) throws IOException { Object value = getter.get(message); if (value == null) { value = schema.newMessage(); setter.set(message, value); } input.mergeObject(value, schema); return input.readFieldNumber(); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/PropertyWrapperAsFieldSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.MessageReadSchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.MessageWriteSchema; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; /** * */ public class PropertyWrapperAsFieldSchema extends FieldSchema { private final SchemaEx schema; private final FieldSchema fieldSchema; protected final Getter getter; protected final Setter setter; public PropertyWrapperAsFieldSchema(Field protoField, PropertyDescriptor propertyDescriptor, SchemaEx schema) { super(protoField, propertyDescriptor.getJavaType()); this.schema = schema; this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); if (schema instanceof MessageWriteSchema) { fieldSchema = ((MessageWriteSchema) schema).getMainPojoFieldMaps().getFieldByNumber(1); return; } fieldSchema = ((MessageReadSchema) schema).getFieldMap().getFieldByNumber(1); } @Override public void getAndWriteTo(OutputEx output, T message) throws IOException { output.writeObject(tag, tagSize, message, fieldSchema::writeTo); } @Override public void writeTo(OutputEx output, Object value) throws IOException { output.writeObject(tag, tagSize, value, fieldSchema::writeTo); } @Override public int mergeFrom(InputEx input, T message) throws IOException { PropertyWrapper wrapper = new PropertyWrapper<>(); input.mergeObject(wrapper, schema); setter.set(message, wrapper.getValue()); return input.readFieldNumber(); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isAnyField; import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isWrapProperty; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.ClassUtils; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.bean.MapGetter; import org.apache.servicecomb.foundation.common.utils.bean.MapSetter; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnySchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.map.MapEntry; import org.apache.servicecomb.foundation.protobuf.internal.schema.map.MapSchema; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.Proto; import io.protostuff.runtime.FieldMapEx; import io.protostuff.runtime.FieldSchema; public abstract class SchemaManager { protected final ProtoMapper protoMapper; protected final Proto proto; // key is canonical message name + ":" + canonical type name protected final Map> canonicalSchemas = new ConcurrentHashMapEx<>(); // key is canonical message name + ":" + canonical type name protected final Map> canonicalSchemasCreated = new ConcurrentHashMapEx<>(); public SchemaManager(ProtoMapper protoMapper) { this.protoMapper = protoMapper; this.proto = protoMapper.getProto(); } protected String generateCacheKey(Message message, JavaType javaType) { return message.getCanonicalName() + ":" + javaType.toCanonical(); } protected abstract SchemaEx newMessageSchema(Message message, JavaType javaType); protected abstract SchemaEx newMessageSchema(Message message, Map types); protected abstract FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor); @SuppressWarnings("unchecked") protected SchemaEx getOrCreateMessageSchema(Message message, Map types) { String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, key -> createMessageSchema(message, types)); } @SuppressWarnings("unchecked") protected SchemaEx getOrCreateMessageSchema(Message message, JavaType javaType) { String cacheKey = generateCacheKey(message, javaType); return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, key -> createMessageSchema(message, javaType)); } @SuppressWarnings("unchecked") protected SchemaEx createMessageSchema(Message message, Map types) { String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); if (schema != null) { return schema; } schema = newMessageSchema(message, types); canonicalSchemas.put(cacheKey, schema); schema.init(); return schema; } @SuppressWarnings("unchecked") protected SchemaEx createMessageSchema(Message message, JavaType javaType) { String cacheKey = generateCacheKey(message, javaType); SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); if (schema != null) { return schema; } schema = newMessageSchema(message, javaType); canonicalSchemas.put(cacheKey, schema); schema.init(); return schema; } protected FieldSchema createMapFieldSchema(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (javaType.isJavaLangObject()) { javaType = ProtoConst.MAP_TYPE; } JavaType entryType = TypeFactory.defaultInstance().constructParametricType(MapEntry.class, javaType.getKeyType(), javaType.getContentType()); SchemaEx> entrySchema = createMessageSchema((Message) protoField.getType(), entryType); return new MapSchema<>(protoField, propertyDescriptor, entrySchema); } // normal message write from or read to a map public FieldMapEx> createMapFields(Message message) { List>> fieldSchemas = new ArrayList<>(); for (Field protoField : message.getFields()) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); propertyDescriptor.setJavaType(ProtoConst.OBJECT_TYPE); propertyDescriptor.setGetter(new MapGetter<>(protoField.getName())); propertyDescriptor.setSetter(new MapSetter<>(protoField.getName())); FieldSchema> fieldSchema = createSchemaField(protoField, propertyDescriptor); fieldSchemas.add(fieldSchema); } return FieldMapEx.createFieldMap(fieldSchemas); } public FieldMapEx> createMapFields(Message message, Map types) { List>> fieldSchemas = new ArrayList<>(); for (Field protoField : message.getFields()) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); JavaType javaType = getParameterType(types, protoField.getName()); if (javaType.isPrimitive()) { javaType = TypeFactory.defaultInstance().constructType(ClassUtils.primitiveToWrapper(javaType.getRawClass())); } propertyDescriptor.setJavaType(javaType); propertyDescriptor.setGetter(new MapGetter<>(protoField.getName())); propertyDescriptor.setSetter(new MapSetter<>(protoField.getName())); FieldSchema> fieldSchema = createSchemaField(protoField, propertyDescriptor); fieldSchemas.add(fieldSchema); } return FieldMapEx.createFieldMap(fieldSchemas); } private JavaType getParameterType(Map types, String parameterName) { if (types.get(parameterName) != null) { return TypeFactory.defaultInstance().constructType(types.get(parameterName)); } throw new IllegalArgumentException( String.format("not found type info for parameter name [%s]", parameterName)); } public FieldSchema createSchemaField(Field protoField, PropertyDescriptor propertyDescriptor) { // map is a special repeated if (protoField.isMap()) { return createMapFieldSchema(protoField, propertyDescriptor); } if (protoField.isRepeated()) { return createRepeatedSchema(protoField, propertyDescriptor); } if (isAnyField(protoField)) { return new AnySchema<>(protoMapper, protoField, propertyDescriptor); } if (protoField.getType().isScalar()) { return createScalarField(protoField, propertyDescriptor); } // message if (protoField.getType().isMessage()) { SchemaEx messageSchema = createMessageSchema((Message) protoField.getType(), propertyDescriptor.getJavaType()); if (isWrapProperty((Message) protoField.getType())) { return new PropertyWrapperAsFieldSchema<>(protoField, propertyDescriptor, messageSchema); } return new MessageAsFieldSchema<>(protoField, propertyDescriptor, messageSchema); } if (protoField.isOneofPart()) { throw new IllegalStateException("not IMPL oneof now."); } ProtoUtils.throwNotSupportWrite(protoField, propertyDescriptor.getJavaType().getRawClass()); return null; } protected abstract FieldSchema createRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor); } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.any; public class AnyEntry { private String typeUrl; private byte[] value; public String getTypeUrl() { return typeUrl; } public void setTypeUrl(String typeUrl) { this.typeUrl = typeUrl; } public byte[] getValue() { return value; } public void setValue(byte[] value) { this.value = value; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.any; import java.io.IOException; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.SchemaWriter; import io.protostuff.WireFormat; import io.protostuff.compiler.model.Message; public class AnyEntrySchema implements SchemaEx { private final ProtoMapper protoMapper; // key is message short name private final Map> anyEntrySserializers = new ConcurrentHashMapEx<>(); // key is message canonical name private final Map> rootDeserializers = new ConcurrentHashMapEx<>(); private final int keyTag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); private final int valueTag = WireFormat.makeTag(2, WireFormat.WIRETYPE_LENGTH_DELIMITED); private final Type anyTargetType; public AnyEntrySchema(ProtoMapper protoMapper, Type type) { this.protoMapper = protoMapper; this.anyTargetType = type; } @Override public void init() { } @Override public Object newMessage() { return new PropertyWrapper<>(); } @Override @SuppressWarnings({"rawtypes", "unchecked"}) public void mergeFrom(InputEx input, Object message) throws IOException { input.readFieldNumber(); String typeUrl = input.readString(); input.readFieldNumber(); byte[] bytes = input.readByteArray(); input.readFieldNumber(); if (message instanceof PropertyWrapper) { if (typeUrl.startsWith(ProtoConst.PACK_SCHEMA)) { ((PropertyWrapper) message).setValue(standardUnpack(typeUrl, bytes)); } else { ((PropertyWrapper) message).setValue(jsonExtendMergeFrom(typeUrl, bytes)); } } else if (message instanceof AnyEntry) { ((AnyEntry) message).setTypeUrl(typeUrl); ((AnyEntry) message).setValue(bytes); } } public Object deserialize(InputEx input) throws IOException { AnyEntry anyEntry = new AnyEntry(); input.mergeObject(anyEntry, this); if (anyEntry.getTypeUrl().startsWith(ProtoConst.PACK_SCHEMA)) { return standardUnpack(anyEntry.getTypeUrl(), anyEntry.getValue()); } return jsonExtendMergeFrom(anyEntry.getTypeUrl(), anyEntry.getValue()); } @SuppressWarnings("unchecked") protected Object standardUnpack(String typeUrl, byte[] bytes) throws IOException { String msgCanonicalName = typeUrl.substring(ProtoConst.PACK_SCHEMA.length()); RootDeserializer valueDeserializer = rootDeserializers .computeIfAbsent(msgCanonicalName, this::createRootDeserializerFromCanonicaName); Object value = valueDeserializer.deserialize(bytes); if (value instanceof Map) { ((Map) value).put(ProtoConst.JSON_ID_NAME, valueDeserializer.getSchema().messageName()); } return value; } protected RootDeserializer createRootDeserializerFromCanonicaName(String msgCanonicalName) { Message message = protoMapper.getMessageFromCanonicaName(msgCanonicalName); if (message == null) { throw new IllegalStateException( "can not find proto message to create deserializer, name=" + msgCanonicalName); } JavaType javaType = protoMapper.getAnyTypes() .getOrDefault(msgCanonicalName, constructRuntimeType(ProtoConst.MAP_TYPE)); return protoMapper.createRootDeserializer(message, javaType); } protected Object jsonExtendMergeFrom(String typeUrl, byte[] bytes) throws IOException { try { return protoMapper.getJsonMapper() .readValue(bytes, Class.forName(typeUrl.substring(ProtoConst.JSON_SCHEMA.length()))); } catch (ClassNotFoundException e) { return protoMapper.getJsonMapper() .readValue(bytes, constructRuntimeType(ProtoConst.OBJECT_TYPE)); } } private JavaType constructRuntimeType(JavaType defaultType) { if (this.anyTargetType == null) { return defaultType; } else { return TypeFactory.defaultInstance().constructType(anyTargetType); } } protected String getInputActualTypeName(Object input) { if (!(input instanceof Map)) { return input.getClass().getSimpleName(); } // @JsonTypeInfo(use = Id.NAME) Object actualTypeName = ((Map) input).get(ProtoConst.JSON_ID_NAME); if (actualTypeName instanceof String) { return (String) actualTypeName; } return null; } /** *
   * if message is type of CustomGeneric<User>
   * we can not get any information of "User" from message.getClass()
   *
   * when use with ServiceComb
   * proto definition convert from swagger, the proto type will be "CustomGenericUser"
   * is not match to "CustomGeneric"
   * so message will be serialized with json schema
   * 
* @param output * @param value * @throws IOException */ @Override public void writeTo(OutputEx output, Object value) throws IOException { String actualTypeName = getInputActualTypeName(value); SchemaWriter entryWriter = actualTypeName == null ? this::jsonExtend : anyEntrySserializers .computeIfAbsent(actualTypeName, n -> createEntryWriter(n, value)); entryWriter.writeTo(output, value); } private SchemaWriter createEntryWriter(String actualTypeName, Object _value) { Message message = protoMapper.getProto().getMessage(actualTypeName); if (message == null) { // not standard, protobuf can not support or not define this type , just extend return this::jsonExtend; } // standard pack RootSerializer valueSerializer = protoMapper.createRootSerializer(message, _value.getClass()); String valueCanonicalName = message.getCanonicalName(); return (output, value) -> standardPack(output, value, valueCanonicalName, valueSerializer); } protected void standardPack(OutputEx output, Object message, String canonicalName, RootSerializer valueSerializer) throws IOException { output.writeString(keyTag, 1, ProtoConst.PACK_SCHEMA + canonicalName); byte[] bytes = valueSerializer.serialize(message); output.writeByteArray(valueTag, 1, bytes); } protected void jsonExtend(OutputEx output, Object input) throws IOException { output.writeString(keyTag, 1, ProtoConst.JSON_SCHEMA + input.getClass().getName()); byte[] bytes = protoMapper.getJsonMapper().writeValueAsBytes(input); output.writeByteArray(valueTag, 1, bytes); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.any; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class AnySchema extends FieldSchema { private final AnyEntrySchema anyEntrySchema; private final Getter getter; private final Setter setter; public AnySchema(ProtoMapper protoMapper, Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.anyEntrySchema = new AnyEntrySchema(protoMapper, null); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @Override public final int mergeFrom(InputEx input, T message) throws IOException { Object anyValue = anyEntrySchema.deserialize(input); setter.set(message, anyValue); return input.readFieldNumber(); } @Override public void getAndWriteTo(OutputEx output, T message) throws IOException { Object anyEntry = getter.get(message); if (anyEntry == null) { return; } output.writeObject(tag, tagSize, anyEntry, anyEntrySchema); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { output.writeObject(tag, tagSize, value, anyEntrySchema); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer; import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isWrapProperty; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.TypesUtil; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.SchemaManager; import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnyEntrySchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.AnyRepeatedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.BytesRepeatedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.MessageRepeatedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.PropertyWrapperRepeatedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.StringRepeatedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl.BoolNotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl.BoolPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl.DoubleNotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl.DoublePackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums.EnumNotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums.EnumPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl.FloatNotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl.FloatPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Fixed32NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Fixed32PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Int32NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Int32PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SFixed32NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SFixed32PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SInt32NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SInt32PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.UInt32NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.UInt32PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Fixed64NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Fixed64PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Int64NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Int64PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SFixed64NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SFixed64PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SInt64NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SInt64PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.UInt64NotPackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.UInt64PackedReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.BoolReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.BytesReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.DoubleReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.EnumsReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Fixed32ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Fixed64ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.FloatReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Int32ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Int64ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SFixed32ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SFixed64ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SInt32ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SInt64ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.StringReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.UInt32ReadSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.UInt64ReadSchemas; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.ScalarFieldType; import io.protostuff.runtime.FieldSchema; public class DeserializerSchemaManager extends SchemaManager { public DeserializerSchemaManager(ProtoMapper protoMapper) { super(protoMapper); } public RootDeserializer createRootDeserializer(Message message, Map types) { SchemaEx messageSchema = getOrCreateMessageSchema(message, types); return new RootDeserializer<>(messageSchema); } @SuppressWarnings({"rawtypes", "unchecked"}) public RootDeserializer createRootDeserializer(Message message, Type type) { if (ProtoUtils.isAnyMessage(message)) { SchemaEx messageSchema = new AnyEntrySchema(protoMapper, type); return new RootDeserializer(messageSchema); } JavaType javaType = TypeFactory.defaultInstance().constructType(type); SchemaEx messageSchema = getOrCreateMessageSchema(message, javaType); return new RootDeserializer<>(messageSchema); } @Override protected SchemaEx newMessageSchema(Message message, Map types) { return new MessageReadSchema<>(protoMapper, message, types); } @Override protected SchemaEx newMessageSchema(Message message, JavaType javaType) { if (ProtoUtils.isWrapProperty(message) && javaType.getRawClass() != PropertyWrapper.class) { Field protoField = message.getField(1); if (javaType.isJavaLangObject()) { javaType = protoField.isRepeated() && !protoField.isMap() ? ProtoConst.LIST_TYPE : ProtoConst.MAP_TYPE; } if (javaType.isPrimitive()) { javaType = TypeFactory.defaultInstance() .constructParametricType(PropertyWrapper.class, TypesUtil.primitiveJavaTypeToWrapper(javaType)); } else { javaType = TypeFactory.defaultInstance().constructParametricType(PropertyWrapper.class, javaType); } } if (javaType.isJavaLangObject()) { javaType = ProtoConst.MAP_TYPE; } return new MessageReadSchema<>(protoMapper, message, javaType); } protected FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor) { if (protoField.getType().isEnum()) { return EnumsReadSchemas.create(protoField, propertyDescriptor); } switch ((ScalarFieldType) protoField.getType()) { case INT32: return Int32ReadSchemas.create(protoField, propertyDescriptor); case UINT32: return UInt32ReadSchemas.create(protoField, propertyDescriptor); case SINT32: return SInt32ReadSchemas.create(protoField, propertyDescriptor); case FIXED32: return Fixed32ReadSchemas.create(protoField, propertyDescriptor); case SFIXED32: return SFixed32ReadSchemas.create(protoField, propertyDescriptor); case INT64: return Int64ReadSchemas.create(protoField, propertyDescriptor); case UINT64: return UInt64ReadSchemas.create(protoField, propertyDescriptor); case SINT64: return SInt64ReadSchemas.create(protoField, propertyDescriptor); case FIXED64: return Fixed64ReadSchemas.create(protoField, propertyDescriptor); case SFIXED64: return SFixed64ReadSchemas.create(protoField, propertyDescriptor); case FLOAT: return FloatReadSchemas.create(protoField, propertyDescriptor); case DOUBLE: return DoubleReadSchemas.create(protoField, propertyDescriptor); case BOOL: return BoolReadSchemas.create(protoField, propertyDescriptor); case STRING: return StringReadSchemas.create(protoField, propertyDescriptor); case BYTES: return BytesReadSchemas.create(protoField, propertyDescriptor); default: throw new IllegalStateException("unknown proto field type: " + protoField.getType()); } } @Override protected FieldSchema createRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { boolean packed = ProtoUtils.isPacked(protoField); if (protoField.getType().isEnum()) { return packed ? EnumPackedReadSchemas.create(protoField, propertyDescriptor) : EnumNotPackedReadSchemas.create(protoField, propertyDescriptor); } if (protoField.getType().isScalar()) { switch ((ScalarFieldType) protoField.getType()) { case INT32: return packed ? Int32PackedReadSchemas.create(protoField, propertyDescriptor) : Int32NotPackedReadSchemas.create(protoField, propertyDescriptor); case UINT32: return packed ? UInt32PackedReadSchemas.create(protoField, propertyDescriptor) : UInt32NotPackedReadSchemas.create(protoField, propertyDescriptor); case SINT32: return packed ? SInt32PackedReadSchemas.create(protoField, propertyDescriptor) : SInt32NotPackedReadSchemas.create(protoField, propertyDescriptor); case FIXED32: return packed ? Fixed32PackedReadSchemas.create(protoField, propertyDescriptor) : Fixed32NotPackedReadSchemas.create(protoField, propertyDescriptor); case SFIXED32: return packed ? SFixed32PackedReadSchemas.create(protoField, propertyDescriptor) : SFixed32NotPackedReadSchemas.create(protoField, propertyDescriptor); case INT64: return packed ? Int64PackedReadSchemas.create(protoField, propertyDescriptor) : Int64NotPackedReadSchemas.create(protoField, propertyDescriptor); case UINT64: return packed ? UInt64PackedReadSchemas.create(protoField, propertyDescriptor) : UInt64NotPackedReadSchemas.create(protoField, propertyDescriptor); case SINT64: return packed ? SInt64PackedReadSchemas.create(protoField, propertyDescriptor) : SInt64NotPackedReadSchemas.create(protoField, propertyDescriptor); case FIXED64: return packed ? Fixed64PackedReadSchemas.create(protoField, propertyDescriptor) : Fixed64NotPackedReadSchemas.create(protoField, propertyDescriptor); case SFIXED64: return packed ? SFixed64PackedReadSchemas.create(protoField, propertyDescriptor) : SFixed64NotPackedReadSchemas.create(protoField, propertyDescriptor); case FLOAT: return packed ? FloatPackedReadSchemas.create(protoField, propertyDescriptor) : FloatNotPackedReadSchemas.create(protoField, propertyDescriptor); case DOUBLE: return packed ? DoublePackedReadSchemas.create(protoField, propertyDescriptor) : DoubleNotPackedReadSchemas.create(protoField, propertyDescriptor); case BOOL: return packed ? BoolPackedReadSchemas.create(protoField, propertyDescriptor) : BoolNotPackedReadSchemas.create(protoField, propertyDescriptor); case STRING: return StringRepeatedReadSchemas.create(protoField, propertyDescriptor); case BYTES: return BytesRepeatedReadSchemas.create(protoField, propertyDescriptor); default: ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); } } if (ProtoUtils.isAnyField(protoField)) { AnyEntrySchema anyEntrySchema = new AnyEntrySchema(protoMapper, null); return AnyRepeatedReadSchemas.create(protoField, propertyDescriptor, anyEntrySchema); } if (protoField.getType().isMessage()) { JavaType contentType = propertyDescriptor.getJavaType().getContentType(); if (contentType == null) { contentType = ProtoConst.OBJECT_TYPE; } SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); if (isWrapProperty((Message) protoField.getType())) { return PropertyWrapperRepeatedReadSchemas.create(protoField, propertyDescriptor, contentSchema); } return MessageRepeatedReadSchemas.create(protoField, propertyDescriptor, contentSchema); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/MessageReadSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Message; import io.protostuff.runtime.FieldMapEx; import io.protostuff.runtime.FieldSchema; import io.protostuff.runtime.RuntimeEnv; import io.protostuff.runtime.RuntimeEnv.Instantiator; /** *
 * map.put("user", new User())
 * root write from map, but user should write from pojo
 * so one schema should support dynamic and concrete logic at the same time
 * 
*/ public class MessageReadSchema implements SchemaEx { private static final Logger LOGGER = LoggerFactory.getLogger(MessageReadSchema.class); protected final ProtoMapper protoMapper; protected final Message message; private FieldMapEx fieldMap; private final Instantiator instantiator; private JavaType javaType; Map argumentsTypes; private boolean argumentsRoot = false; @SuppressWarnings("unchecked") public MessageReadSchema(ProtoMapper protoMapper, Message message, Map argumentsTypes) { this.argumentsRoot = true; this.protoMapper = protoMapper; this.message = message; this.argumentsTypes = argumentsTypes; this.instantiator = RuntimeEnv.newInstantiator((Class) ProtoConst.MAP_TYPE.getRawClass()); } @SuppressWarnings("unchecked") public MessageReadSchema(ProtoMapper protoMapper, Message message, JavaType javaType) { this.protoMapper = protoMapper; this.message = message; this.javaType = javaType; if (javaType.isJavaLangObject() || Map.class.isAssignableFrom(javaType.getRawClass())) { javaType = ProtoConst.MAP_TYPE; } this.instantiator = RuntimeEnv.newInstantiator((Class) javaType.getRawClass()); } public Message getMessage() { return message; } @Override public T newMessage() { return instantiator.newInstance(); } @Override public String messageName() { return message.getName(); } public FieldMapEx getFieldMap() { return fieldMap; } @SuppressWarnings("unchecked") @Override public void init() { if (argumentsRoot) { this.fieldMap = (FieldMapEx) protoMapper.getDeserializerSchemaManager() .createMapFields(message, argumentsTypes); return; } if (Map.class.isAssignableFrom(javaType.getRawClass())) { this.fieldMap = (FieldMapEx) protoMapper.getDeserializerSchemaManager() .createMapFields(message); return; } this.createFieldMap(); } private void createFieldMap() { DeserializerSchemaManager deserializerSchemaManager = protoMapper.getDeserializerSchemaManager(); BeanDescriptor beanDescriptor = protoMapper.getBeanDescriptorManager().getOrCreateBeanDescriptor(javaType); List> fieldSchemas = new ArrayList<>(); for (PropertyDescriptor propertyDescriptor : beanDescriptor.getPropertyDescriptors().values()) { Field protoField = message.getField(propertyDescriptor.getName()); if (protoField == null) { LOGGER.info("java field {}:{} not exist in proto message {}, ignore it.", beanDescriptor.getJavaType().getRawClass().getName(), propertyDescriptor.getName(), message.getCanonicalName()); continue; } if (propertyDescriptor.getSetter() == null) { LOGGER.info("no setter for java field {}:{} in proto message {}, ignore it.", beanDescriptor.getJavaType().getRawClass().getName(), propertyDescriptor.getName(), message.getCanonicalName()); continue; } FieldSchema fieldSchema = deserializerSchemaManager.createSchemaField(protoField, propertyDescriptor); fieldSchemas.add(fieldSchema); } this.fieldMap = FieldMapEx.createFieldMap(fieldSchemas); } @Override public void mergeFrom(InputEx input, T message) throws IOException { FieldSchema fieldSchema = null; try { for (int n = input.readFieldNumber(); n != 0; ) { fieldSchema = fieldMap.getFieldByNumber(n); if (fieldSchema != null) { n = fieldSchema.mergeFrom(input, message); continue; } input.handleUnknownField(n); n = input.readFieldNumber(); } } catch (Throwable e) { if (fieldSchema != null) { Field protoField = fieldSchema.getProtoField(); LOGGER.error("Failed to mergeFrom, field={}:{}, type={}, error {}", protoField.getType().getCanonicalName(), protoField.getName(), protoField.getTypeName(), e.getMessage()); } throw e; } } @Override public void writeTo(OutputEx output, Object value) throws IOException { throw new UnsupportedOperationException(); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractPrimitiveReaders.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated; import io.protostuff.compiler.model.Field; public abstract class AbstractPrimitiveReaders extends AbstractReaders { public RepeatedReader> primitiveArrayReader; @SuppressWarnings("unchecked") public AbstractPrimitiveReaders(Field protoField) { super(protoField); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractReaders.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated; import static org.apache.servicecomb.foundation.common.utils.ReflectUtils.getFieldArgument; import java.util.Collection; import io.protostuff.compiler.model.Field; public abstract class AbstractReaders { protected final Field protoField; protected final int fieldNumber; public RepeatedReader> collectionReader; public final Class arrayClass; @SuppressWarnings("unchecked") public AbstractReaders(Field protoField) { this(protoField, null); } public AbstractReaders(Field protoField, Class arrayClass) { this.protoField = protoField; this.fieldNumber = protoField.getTag(); if (arrayClass == null) { arrayClass = getFieldArgument(this.getClass(), "arrayClass"); } this.arrayClass = arrayClass; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/PrimitiveArrayBuilderWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; public class PrimitiveArrayBuilderWrapper { private final PrimitiveArrayBuilder builder; private T array; public PrimitiveArrayBuilderWrapper(PrimitiveArrayBuilder builder) { this.builder = builder; } public T getArray() { return array; } public void setArray(T array) { this.array = array; } public PrimitiveArrayBuilder getBuilder() { return builder; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class RepeatedReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractReaders readers) { JavaType javaType = propertyDescriptor.getJavaType(); if (readers.arrayClass.isAssignableFrom(javaType.getRawClass())) { return new ArrayRepeatedSchema<>(protoField, propertyDescriptor, readers); } if (Collection.class.isAssignableFrom(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new CollectionRepeatedSchema<>(protoField, propertyDescriptor, readers); } ProtoUtils.throwNotSupportMerge(protoField, javaType); return null; } static class AbstractRepeatedSchema extends FieldSchema { protected final AbstractReaders readers; public AbstractRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractReaders readers) { super(protoField, propertyDescriptor.getJavaType()); this.readers = readers; } } static class CollectionRepeatedSchema extends AbstractRepeatedSchema { private final Getter> getter; private final Setter> setter; public CollectionRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractReaders readers) { super(protoField, propertyDescriptor, readers); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @Override public final int mergeFrom(InputEx input, T message) throws IOException { Collection collection = getter.get(message); if (collection == null) { if (Set.class.equals(javaType.getRawClass())) { collection = new HashSet<>(); } else { collection = new ArrayList<>(); } setter.set(message, collection); } return readers.collectionReader.read(input, collection); } } static class ArrayRepeatedSchema extends AbstractRepeatedSchema { private final Getter getter; private final Setter setter; public ArrayRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractReaders readers) { super(protoField, propertyDescriptor, readers); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @SuppressWarnings("unchecked") @Override public final int mergeFrom(InputEx input, T message) throws IOException { ELE_TYPE[] array = getter.get(message); Collection collection = array == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(array)); int fieldNumber = readers.collectionReader.read(input, collection); ELE_TYPE[] newArray = (ELE_TYPE[]) Array.newInstance(readers.arrayClass.getComponentType(), collection.size()); setter.set(message, collection.toArray(newArray)); return fieldNumber; } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated; import java.io.IOException; import io.protostuff.InputEx; public interface RepeatedReader { int read(InputEx input, T value) throws IOException; } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/AnyRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnyEntrySchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class AnyRepeatedReadSchemas { private static class MessageReaders extends AbstractReaders { public MessageReaders(Field protoField, AnyEntrySchema anyEntrySchema) { super(protoField); collectionReader = (input, collection) -> { while (true) { Object value = anyEntrySchema.deserialize(input); collection.add(value); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AnyEntrySchema anyEntrySchema) { return RepeatedReadSchemas.create(protoField, propertyDescriptor, new MessageReaders(protoField, anyEntrySchema)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/BytesRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BytesRepeatedReadSchemas { private static class BytesReaders extends AbstractReaders { public BytesReaders(Field protoField) { super(protoField); collectionReader = (input, collection) -> { while (true) { collection.add(input.readByteArray()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedReadSchemas.create(protoField, propertyDescriptor, new BytesReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/MessageRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import io.protostuff.SchemaReader; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class MessageRepeatedReadSchemas { private static class MessageReaders extends AbstractReaders { public MessageReaders(Field protoField, SchemaReader elementSchema) { super(protoField); collectionReader = (input, collection) -> { while (true) { Object value = input.mergeObject(null, elementSchema); collection.add(value); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, SchemaReader elementSchema) { return RepeatedReadSchemas.create(protoField, propertyDescriptor, new MessageReaders(protoField, elementSchema)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/PropertyWrapperRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import io.protostuff.SchemaReader; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class PropertyWrapperRepeatedReadSchemas { private static class PropertyWrapperReaders extends AbstractReaders { public PropertyWrapperReaders(Field protoField, SchemaReader elementSchema) { super(protoField); collectionReader = (input, collection) -> { while (true) { PropertyWrapper wrapper = new PropertyWrapper<>(); input.mergeObject(wrapper, elementSchema); collection.add(wrapper.getValue()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, SchemaReader elementSchema) { return RepeatedReadSchemas .create(protoField, propertyDescriptor, new PropertyWrapperReaders(protoField, elementSchema)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/StringRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class StringRepeatedReadSchemas { private static class StringReaders extends AbstractReaders { public StringReaders(Field protoField) { super(protoField); collectionReader = (input, collection) -> { while (true) { collection.add(input.readString()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedReadSchemas.create(protoField, propertyDescriptor, new StringReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/BoolRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolRepeatedReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { JavaType javaType = propertyDescriptor.getJavaType(); if (boolean[].class.equals(javaType.getRawClass())) { return new BoolPrimitiveArray<>(protoField, propertyDescriptor, readers); } return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); } static class BoolPrimitiveArray extends FieldSchema { private final Getter getter; private final Setter setter; private final AbstractPrimitiveReaders readers; public BoolPrimitiveArray(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); this.readers = readers; } @Override public int mergeFrom(InputEx input, T message) throws IOException { PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( input.getArrayBuilders().getBooleanBuilder()); int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); boolean[] newValue = builderWrapper.getArray(); newValue = mergeArray(getter.get(message), newValue); setter.set(message, newValue); return fieldNumber; } private boolean[] mergeArray(boolean[] oldValue, boolean[] newValue) { if (oldValue == null || oldValue.length == 0) { return newValue; } return concatArray(oldValue, newValue); } private boolean[] concatArray(boolean[] oldValue, boolean[] newValue) { int len1 = oldValue.length; int len2 = newValue.length; boolean[] result = Arrays.copyOf(oldValue, len1 + len2); System.arraycopy(newValue, 0, result, len1, len2); return result; } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolNotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.BoolRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolNotPackedReadSchemas { private static class BoolNotPackedReaders extends AbstractPrimitiveReaders { public BoolNotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); boolean[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readBool(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readBool()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return BoolRepeatedReadSchemas.create(protoField, propertyDescriptor, new BoolNotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.BoolRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolPackedReadSchemas { private static class BoolPackedReaders extends AbstractPrimitiveReaders { public BoolPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); boolean[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedBool(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedBool()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return BoolRepeatedReadSchemas.create(protoField, propertyDescriptor, new BoolPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/DoubleRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoubleRepeatedReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { JavaType javaType = propertyDescriptor.getJavaType(); if (double[].class.equals(javaType.getRawClass())) { return new PrimitiveArraySchema<>(protoField, propertyDescriptor, readers); } return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); } static class PrimitiveArraySchema extends FieldSchema { private final Getter getter; private final Setter setter; private final AbstractPrimitiveReaders readers; public PrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); this.readers = readers; } @Override public int mergeFrom(InputEx input, T message) throws IOException { PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( input.getArrayBuilders().getDoubleBuilder()); int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); double[] newValue = builderWrapper.getArray(); newValue = mergeArray(getter.get(message), newValue); setter.set(message, newValue); return fieldNumber; } public double[] mergeArray(double[] oldValue, double[] newValue) { if (oldValue == null || oldValue.length == 0) { return newValue; } return concatArray(oldValue, newValue); } private double[] concatArray(double[] oldValue, double[] newValue) { int len1 = oldValue.length; int len2 = newValue.length; double[] result = Arrays.copyOf(oldValue, len1 + len2); System.arraycopy(newValue, 0, result, len1, len2); return result; } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoubleNotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.DoubleRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoubleNotPackedReadSchemas { private static class NotPackedReaders extends AbstractPrimitiveReaders { public NotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); double[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readDouble(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readDouble()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return DoubleRepeatedReadSchemas.create(protoField, propertyDescriptor, new NotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoublePackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.DoubleRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoublePackedReadSchemas { private static class DoublePackedReaders extends AbstractPrimitiveReaders { public DoublePackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); double[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedDouble(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedDouble()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return DoubleRepeatedReadSchemas.create(protoField, propertyDescriptor, new DoublePackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumNotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import io.protostuff.runtime.FieldSchema; public class EnumNotPackedReadSchemas { private static class EnumNotPackedReaders extends AbstractReaders> { private final EnumMeta enumMeta; public EnumNotPackedReaders(Field protoField, JavaType javaType) { super(protoField, EnumSchemaUtils.constructEnumArrayClass(javaType)); this.enumMeta = new EnumMeta(protoField, javaType.getContentType()); collectionReader = (input, collection) -> { while (true) { int value = input.readEnum(); Enum enumValue = enumMeta.getEnumByValue(value); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum value %d for %s, proto field=%s:%s", value, javaType.getRawClass().getName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } collection.add(enumValue); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } private static class EnumIntNotPackedSchema extends FieldSchema { private final Getter> getter; private final Setter> setter; public EnumIntNotPackedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { Collection collection = getter.get(message); if (collection == null) { collection = new ArrayList<>(); setter.set(message, collection); } while (true) { int value = input.readEnum(); collection.add(value); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (javaType.isJavaLangObject()) { return new EnumIntNotPackedSchema<>(protoField, propertyDescriptor); } return RepeatedReadSchemas .create(protoField, propertyDescriptor, new EnumNotPackedReaders(protoField, propertyDescriptor.getJavaType())); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import io.protostuff.runtime.FieldSchema; public class EnumPackedReadSchemas { private static class EnumPackedReaders extends AbstractReaders> { private final EnumMeta enumMeta; public EnumPackedReaders(Field protoField, JavaType javaType) { super(protoField, EnumSchemaUtils.constructEnumArrayClass(javaType)); this.enumMeta = new EnumMeta(protoField, javaType.getContentType()); collectionReader = (input, collection) -> { while (true) { int value = input.readPackedEnum(); Enum enumValue = enumMeta.getEnumByValue(value); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum value %d for %s, proto field=%s:%s", value, javaType.getRawClass().getName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } collection.add(enumValue); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } private static class EnumIntPackedSchema extends FieldSchema { private final Getter> getter; private final Setter> setter; public EnumIntPackedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { Collection collection = getter.get(message); if (collection == null) { collection = new ArrayList<>(); setter.set(message, collection); } while (true) { int value = input.readPackedEnum(); collection.add(value); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (javaType.isJavaLangObject()) { return new EnumIntPackedSchema<>(protoField, propertyDescriptor); } return RepeatedReadSchemas .create(protoField, propertyDescriptor, new EnumPackedReaders(protoField, propertyDescriptor.getJavaType())); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumSchemaUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import com.fasterxml.jackson.databind.JavaType; public final class EnumSchemaUtils { private EnumSchemaUtils() { } public static T constructEnumArrayClass(JavaType javaType) { if (javaType.isArrayType()) { return ReflectUtils.constructArrayType(javaType.getContentType().getRawClass()); } return ReflectUtils.constructArrayType(Object.class); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/FloatRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatRepeatedReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { JavaType javaType = propertyDescriptor.getJavaType(); if (float[].class.equals(javaType.getRawClass())) { return new FloatPrimitiveArraySchema<>(protoField, propertyDescriptor, readers); } return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); } static class FloatPrimitiveArraySchema extends FieldSchema { private final Getter getter; private final Setter setter; private final AbstractPrimitiveReaders readers; public FloatPrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); this.readers = readers; } @Override public int mergeFrom(InputEx input, T message) throws IOException { PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( input.getArrayBuilders().getFloatBuilder()); int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); float[] newValue = builderWrapper.getArray(); newValue = mergeArray(getter.get(message), newValue); setter.set(message, newValue); return fieldNumber; } public float[] mergeArray(float[] oldValue, float[] newValue) { if (oldValue == null || oldValue.length == 0) { return newValue; } return concatArray(oldValue, newValue); } private float[] concatArray(float[] oldValue, float[] newValue) { int len1 = oldValue.length; int len2 = newValue.length; float[] result = Arrays.copyOf(oldValue, len1 + len2); System.arraycopy(newValue, 0, result, len1, len2); return result; } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatNotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.FloatRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatNotPackedReadSchemas { private static class FloatNotPackedReaders extends AbstractPrimitiveReaders { public FloatNotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); float[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readFloat(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readFloat()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return FloatRepeatedReadSchemas.create(protoField, propertyDescriptor, new FloatNotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.FloatRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatPackedReadSchemas { private static class FloatPackedReaders extends AbstractPrimitiveReaders { public FloatPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); float[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedFloat(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedFloat()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return FloatRepeatedReadSchemas.create(protoField, propertyDescriptor, new FloatPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/IntRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class IntRepeatedReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { JavaType javaType = propertyDescriptor.getJavaType(); if (int[].class.equals(javaType.getRawClass())) { return new IntPrimitiveArraySchema<>(protoField, propertyDescriptor, readers); } return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); } static class IntPrimitiveArraySchema extends FieldSchema { private final Getter getter; private final Setter setter; private final AbstractPrimitiveReaders readers; public IntPrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); this.readers = readers; } @Override public int mergeFrom(InputEx input, T message) throws IOException { PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( input.getArrayBuilders().getIntBuilder()); int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); int[] newValue = builderWrapper.getArray(); newValue = mergeArray(getter.get(message), newValue); setter.set(message, newValue); return fieldNumber; } public int[] mergeArray(int[] oldValue, int[] newValue) { if (oldValue == null || oldValue.length == 0) { return newValue; } return concatArray(oldValue, newValue); } private int[] concatArray(int[] oldValue, int[] newValue) { int len1 = oldValue.length; int len2 = newValue.length; int[] result = Arrays.copyOf(oldValue, len1 + len2); System.arraycopy(newValue, 0, result, len1, len2); return result; } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed32NotPackedReadSchemas { private static class NotPackedReaders extends AbstractPrimitiveReaders { public NotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readFixed32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readFixed32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new NotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed32PackedReadSchemas { private static class Fixed32PackedReaders extends AbstractPrimitiveReaders { public Fixed32PackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedFixed32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedFixed32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new Fixed32PackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int32NotPackedReadSchemas { private static class Int32NotPackedReaders extends AbstractPrimitiveReaders { public Int32NotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readInt32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readInt32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new Int32NotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int32PackedReadSchemas { private static class Int32PackedReaders extends AbstractPrimitiveReaders { public Int32PackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedInt32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedInt32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new Int32PackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed32NotPackedReadSchemas { private static class NotPackedReaders extends AbstractPrimitiveReaders { public NotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readSFixed32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readSFixed32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new NotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed32PackedReadSchemas { private static class SFixed32PackedReaders extends AbstractPrimitiveReaders { public SFixed32PackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedSFixed32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedSFixed32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new SFixed32PackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt32NotPackedReadSchemas { private static class SInt32NotPackedReaders extends AbstractPrimitiveReaders { public SInt32NotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readSInt32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readSInt32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new SInt32NotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt32PackedReadSchemas { private static class SInt32PackedReaders extends AbstractPrimitiveReaders { public SInt32PackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedSInt32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedSInt32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new SInt32PackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt32NotPackedReadSchemas { private static class UInt32NotPackedReaders extends AbstractPrimitiveReaders { public UInt32NotPackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readUInt32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readUInt32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new UInt32NotPackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt32PackedReadSchemas { private static class UInt32PackedReaders extends AbstractPrimitiveReaders { public UInt32PackedReaders(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); int[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedUInt32(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedUInt32()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new UInt32PackedReaders(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/LongRepeatedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class LongRepeatedReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { JavaType javaType = propertyDescriptor.getJavaType(); if (long[].class.equals(javaType.getRawClass())) { return new LongPrimitiveArraySchema<>(protoField, propertyDescriptor, readers); } return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); } static class LongPrimitiveArraySchema extends FieldSchema { private final Getter getter; private final Setter setter; private final AbstractPrimitiveReaders readers; public LongPrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveReaders readers) { super(protoField, propertyDescriptor.getJavaType()); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); this.readers = readers; } @Override public int mergeFrom(InputEx input, T message) throws IOException { PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( input.getArrayBuilders().getLongBuilder()); int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); long[] newValue = builderWrapper.getArray(); newValue = mergeArray(getter.get(message), newValue); setter.set(message, newValue); return fieldNumber; } public long[] mergeArray(long[] oldValue, long[] newValue) { if (oldValue == null || oldValue.length == 0) { return newValue; } return concatArray(oldValue, newValue); } private long[] concatArray(long[] oldValue, long[] newValue) { int len1 = oldValue.length; int len2 = newValue.length; long[] result = Arrays.copyOf(oldValue, len1 + len2); System.arraycopy(newValue, 0, result, len1, len2); return result; } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed64NotPackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readFixed64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readFixed64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed64PackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedFixed64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedFixed64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int64NotPackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readInt64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readInt64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int64PackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedInt64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedInt64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed64NotPackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readSFixed64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readSFixed64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed64PackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedSFixed64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedSFixed64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt64NotPackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readSInt64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readSInt64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt64PackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedSInt64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedSInt64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64NotPackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt64NotPackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readUInt64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readUInt64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64PackedReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt64PackedReadSchemas { private static class Readers extends AbstractPrimitiveReaders { public Readers(Field protoField) { super(protoField); this.primitiveArrayReader = (input, builderWrapper) -> { PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); long[] chunk = builder.resetAndStart(); int ix = 0; while (true) { if (ix >= chunk.length) { chunk = builder.appendCompletedChunk(chunk, ix); ix = 0; } chunk[ix++] = input.readPackedUInt64(); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); return fieldNumber; } } }; this.collectionReader = (input, collection) -> { while (true) { collection.add(input.readPackedUInt64()); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/AbstractScalarReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class AbstractScalarReadSchemas { abstract static class AbstractIntSchema extends FieldSchema { protected final Setter setter; public AbstractIntSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } } abstract static class AbstractLongSchema extends FieldSchema { protected final Setter setter; public AbstractLongSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BoolReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.BoolSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (boolean.class.equals(javaType.getRawClass())) { return new BooleanPrimitiveSchema<>(protoField, propertyDescriptor); } if (Boolean.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new BooleanSchema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class BooleanSchema extends FieldSchema { private final Setter setter; public BooleanSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { boolean value = input.readBool(); setter.set(message, value); return input.readFieldNumber(); } } private static class BooleanPrimitiveSchema extends FieldSchema { private final BoolSetter setter; public BooleanPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { boolean value = input.readBool(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BytesReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BytesReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (byte[].class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new BytesSchema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class BytesSchema extends FieldSchema { private final Setter setter; public BytesSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { byte[] value = input.readByteArray(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/DoubleReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.DoubleSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoubleReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (double.class.equals(javaType.getRawClass())) { return new DoublePrimitiveSchema<>(protoField, propertyDescriptor); } if (Double.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new DoubleSchema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class DoubleSchema extends FieldSchema { private final Setter setter; public DoubleSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { double value = input.readDouble(); setter.set(message, value); return input.readFieldNumber(); } } private static class DoublePrimitiveSchema extends FieldSchema { private final DoubleSetter setter; public DoublePrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { double value = input.readDouble(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/EnumsReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.base.DynamicEnum; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import io.protostuff.runtime.FieldSchema; public class EnumsReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (javaType.isEnumType()) { return new EnumSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new IntEnumSchema<>(protoField, propertyDescriptor); } if (int.class.equals(javaType.getRawClass())) { return new IntPrimitiveEnumSchema<>(protoField, propertyDescriptor); } if (javaType.isTypeOrSubTypeOf(DynamicEnum.class)) { return new DynamicEnumSchema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class EnumSchema extends FieldSchema { private final Setter> setter; private final EnumMeta enumMeta; public EnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); enumMeta = new EnumMeta(protoField, javaType); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); Enum enumValue = enumMeta.getEnumByValue(value); if (enumValue != null) { setter.set(message, enumValue); return input.readFieldNumber(); } throw new IllegalStateException( String.format("invalid enum value %d for %s, proto field=%s:%s", value, javaType.getRawClass().getName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } } private static class DynamicEnumSchema extends FieldSchema { public DynamicEnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public int mergeFrom(InputEx input, T message) throws IOException { throw new IllegalStateException( String.format("currently, protobuf not support dynamic enum, type=%s, proto field=%s:%s", javaType.getRawClass().getName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } } private static class IntEnumSchema extends FieldSchema { private final Setter setter; public IntEnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); setter.set(message, value); return input.readFieldNumber(); } } private static class IntPrimitiveEnumSchema extends FieldSchema { private final IntSetter setter; public IntPrimitiveEnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed32ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractIntSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed32ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (int.class.equals(javaType.getRawClass())) { return new Fixed32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new Fixed32Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class Fixed32Schema extends AbstractIntSchema { public Fixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readFixed32(); setter.set(message, value); return input.readFieldNumber(); } } private static class Fixed32PrimitiveSchema extends FieldSchema { protected final IntSetter setter; public Fixed32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readFixed32(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed64ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractLongSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed64ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (long.class.equals(javaType.getRawClass())) { return new Fixed64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new Fixed64Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class Fixed64Schema extends AbstractLongSchema { public Fixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readFixed64(); setter.set(message, value); return input.readFieldNumber(); } } private static class Fixed64PrimitiveSchema extends FieldSchema { protected final LongSetter setter; public Fixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readFixed64(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/FloatReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.FloatSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (float.class.equals(javaType.getRawClass())) { return new FloatPrimitiveSchema<>(protoField, propertyDescriptor); } if (Float.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new FloatSchema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class FloatSchema extends FieldSchema { private final Setter setter; public FloatSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { float value = input.readFloat(); setter.set(message, value); return input.readFieldNumber(); } } private static class FloatPrimitiveSchema extends FieldSchema { private final FloatSetter setter; public FloatPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { float value = input.readFloat(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int32ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.ByteSetter; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.common.utils.bean.ShortSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractIntSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int32ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (int.class.equals(javaType.getRawClass())) { return new IntFieldIntPrimitiveSchema<>(protoField, propertyDescriptor); } if (short.class.equals(javaType.getRawClass())) { return new ShortFieldIntPrimitiveSchema<>(protoField, propertyDescriptor); } if (byte.class.equals(javaType.getRawClass())) { return new ByteFieldIntPrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(javaType.getRawClass()) || Byte.class.equals(javaType.getRawClass()) || Short.class .equals(javaType.getRawClass()) || javaType .isJavaLangObject()) { return new Int32Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class Int32Schema extends AbstractIntSchema { public Int32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); if (Byte.class.equals(javaType.getRawClass())) { setter.set(message, (byte) value); } else if (Short.class.equals(javaType.getRawClass())) { setter.set(message, (short) value); } else { setter.set(message, value); } return input.readFieldNumber(); } } private static class IntFieldIntPrimitiveSchema extends FieldSchema { protected final IntSetter setter; public IntFieldIntPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); setter.set(message, value); return input.readFieldNumber(); } } private static class ShortFieldIntPrimitiveSchema extends FieldSchema { protected final ShortSetter setter; public ShortFieldIntPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); setter.set(message, (short) value); return input.readFieldNumber(); } } private static class ByteFieldIntPrimitiveSchema extends FieldSchema { protected final ByteSetter setter; public ByteFieldIntPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readInt32(); setter.set(message, (byte) value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int64ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Date; import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractLongSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int64ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (long.class.equals(javaType.getRawClass())) { return new LongFiledLongPrimitiveSchema<>(protoField, propertyDescriptor); } return new Int64Schema<>(protoField, propertyDescriptor); } private static class Int64Schema extends AbstractLongSchema { public Int64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readInt64(); if (Date.class.equals(javaType.getRawClass())) { setter.set(message, new Date(value)); } else if (LocalDate.class.equals(javaType.getRawClass())) { setter.set(message, LocalDate.ofEpochDay(value)); } else if (LocalDateTime.class.equals(javaType.getRawClass())) { setter.set(message, LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)); } else { setter.set(message, value); } return input.readFieldNumber(); } } private static class LongFiledLongPrimitiveSchema extends FieldSchema { protected final LongSetter setter; public LongFiledLongPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readInt64(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed32ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractIntSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed32ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (int.class.equals(javaType.getRawClass())) { return new SFixed32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new SFixed32Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class SFixed32Schema extends AbstractIntSchema { public SFixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSFixed32(); setter.set(message, value); return input.readFieldNumber(); } } private static class SFixed32PrimitiveSchema extends FieldSchema { protected final IntSetter setter; public SFixed32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSFixed32(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed64ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractLongSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed64ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (long.class.equals(javaType.getRawClass())) { return new SFixed64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new SFixed64Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class SFixed64Schema extends AbstractLongSchema { public SFixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readSFixed64(); setter.set(message, value); return input.readFieldNumber(); } } private static class SFixed64PrimitiveSchema extends FieldSchema { protected final LongSetter setter; public SFixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readSFixed64(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt32ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.ByteSetter; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.common.utils.bean.ShortSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractIntSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt32ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (int.class.equals(javaType.getRawClass())) { return new SInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (short.class.equals(javaType.getRawClass())) { return new ShortFieldSInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (byte.class.equals(javaType.getRawClass())) { return new ByteFieldSInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(javaType.getRawClass()) || Byte.class.equals(javaType.getRawClass()) || Short.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new SInt32Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class SInt32Schema extends AbstractIntSchema { public SInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSInt32(); if (Byte.class.equals(javaType.getRawClass())) { setter.set(message, (byte) value); } else if (Short.class.equals(javaType.getRawClass())) { setter.set(message, (short) value); } else { setter.set(message, value); } return input.readFieldNumber(); } } private static class SInt32PrimitiveSchema extends FieldSchema { protected final IntSetter setter; public SInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSInt32(); setter.set(message, value); return input.readFieldNumber(); } } private static class ShortFieldSInt32PrimitiveSchema extends FieldSchema { protected final ShortSetter setter; public ShortFieldSInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSInt32(); setter.set(message, (short) value); return input.readFieldNumber(); } } private static class ByteFieldSInt32PrimitiveSchema extends FieldSchema { protected final ByteSetter setter; public ByteFieldSInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readSInt32(); setter.set(message, (byte) value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt64ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractLongSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt64ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (long.class.equals(javaType.getRawClass())) { return new SInt64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new SInt64Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class SInt64Schema extends AbstractLongSchema { public SInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readSInt64(); setter.set(message, value); return input.readFieldNumber(); } } private static class SInt64PrimitiveSchema extends FieldSchema { protected final LongSetter setter; public SInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readSInt64(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/StringReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import org.apache.servicecomb.foundation.common.utils.bean.CharSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class StringReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (char.class.equals(javaType.getRawClass())) { return new CharFieldStringSchema<>(protoField, propertyDescriptor); } if (String.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject() || Character.class .equals(javaType.getRawClass())) { return new StringSchema<>(protoField, propertyDescriptor); } if (BigDecimal.class.equals(javaType.getRawClass())) { return new BigDecimalSchema<>(protoField, propertyDescriptor); } if (BigInteger.class.equals(javaType.getRawClass())) { return new BigIntegerSchema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class StringSchema extends FieldSchema { private final Setter setter; public StringSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { String value = input.readString(); if (char.class .equals(javaType.getRawClass()) || Character.class.equals(javaType.getRawClass())) { setter.set(message, value.toCharArray()[0]); } else { setter.set(message, value); } return input.readFieldNumber(); } } private static class CharFieldStringSchema extends FieldSchema { private final CharSetter setter; public CharFieldStringSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { String value = input.readString(); setter.set(message, value.toCharArray()[0]); return input.readFieldNumber(); } } private static class BigDecimalSchema extends FieldSchema { private final Setter setter; public BigDecimalSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { String value = input.readString(); setter.set(message, new BigDecimal(value)); return input.readFieldNumber(); } } private static class BigIntegerSchema extends FieldSchema { private final Setter setter; public BigIntegerSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { String value = input.readString(); setter.set(message, new BigInteger(value)); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt32ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractIntSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt32ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (int.class.equals(javaType.getRawClass())) { return new UInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new UInt32Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class UInt32Schema extends AbstractIntSchema { public UInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readUInt32(); setter.set(message, value); return input.readFieldNumber(); } } private static class UInt32PrimitiveSchema extends FieldSchema { protected final IntSetter setter; public UInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { int value = input.readUInt32(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt64ReadSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractLongSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt64ReadSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { JavaType javaType = propertyDescriptor.getJavaType(); if (long.class.equals(javaType.getRawClass())) { return new UInt64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { return new UInt64Schema<>(protoField, propertyDescriptor); } ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); return null; } private static class UInt64Schema extends AbstractLongSchema { public UInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readUInt64(); setter.set(message, value); return input.readFieldNumber(); } } private static class UInt64PrimitiveSchema extends FieldSchema { protected final LongSetter setter; public UInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.setter = propertyDescriptor.getSetter(); } @Override public int mergeFrom(InputEx input, T message) throws IOException { long value = input.readUInt64(); setter.set(message, value); return input.readFieldNumber(); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.map; import java.util.Map.Entry; import com.fasterxml.jackson.annotation.JsonSetter; public class MapEntry implements Entry { private K key; private V value; public K getKey() { return key; } public void setKey(K key) { this.key = key; } public V getValue() { return value; } public V setValue(V value) { this.value = value; return value; } @JsonSetter("value") public void valueSetter(V value) { this.value = value; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntrySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.map; import java.io.IOException; import java.util.Map.Entry; import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.MessageReadSchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.MessageWriteSchema; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.runtime.FieldSchema; public class MapEntrySchema implements SchemaEx> { private final FieldSchema> keySchema; private final FieldSchema> valueSchema; public MapEntrySchema(SchemaEx> entrySchema) { if (entrySchema instanceof MessageWriteSchema) { keySchema = ((MessageWriteSchema>) entrySchema).getMainPojoFieldMaps().getFieldByNumber(1); valueSchema = ((MessageWriteSchema>) entrySchema).getMainPojoFieldMaps() .getFieldByNumber(2); return; } keySchema = ((MessageReadSchema>) entrySchema).getFieldMap().getFieldByNumber(1); valueSchema = ((MessageReadSchema>) entrySchema).getFieldMap().getFieldByNumber(2); } @Override public void init() { } @Override public void mergeFrom(InputEx input, Entry message) throws IOException { input.readFieldNumber(); keySchema.mergeFrom(input, message); valueSchema.mergeFrom(input, message); } @Override public void writeTo(OutputEx output, Entry value) throws IOException { keySchema.writeTo(output, value.getKey()); valueSchema.writeTo(output, value.getValue()); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.map; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class MapSchema extends FieldSchema { private final Getter> getter; private final Setter> setter; private final SchemaEx> entrySchema; public MapSchema(Field protoField, PropertyDescriptor propertyDescriptor, SchemaEx> entrySchema) { super(protoField, propertyDescriptor.getJavaType()); this.entrySchema = new MapEntrySchema(entrySchema); this.getter = propertyDescriptor.getGetter(); this.setter = propertyDescriptor.getSetter(); } @Override public final int mergeFrom(InputEx input, T message) throws IOException { Map map = getter.get(message); if (map == null) { map = new LinkedHashMap<>(); setter.set(message, map); } Entry entry = new MapEntry<>(); while (true) { input.mergeObject(entry, entrySchema); map.put(entry.getKey(), entry.getValue()); entry.setValue(null); int fieldNumber = input.readFieldNumber(); if (fieldNumber != this.fieldNumber) { return fieldNumber; } } } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Map map = getter.get(message); if (map == null) { return; } writeMap(output, map); } @SuppressWarnings("unchecked") @Override public final void writeTo(OutputEx output, Object value) throws IOException { writeMap(output, (Map) value); } private void writeMap(OutputEx output, Map map) throws IOException { for (Entry entry : map.entrySet()) { if (entry.getKey() == null || entry.getValue() == null) { continue; } output.writeObject(tag, tagSize, entry, entrySchema); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.InputEx; import io.protostuff.OutputEx; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Message; import io.protostuff.runtime.FieldMapEx; import io.protostuff.runtime.FieldSchema; /** *
 * map.put("user", new User())
 * root write from map, but user should write from pojo
 * so one schema should support dynamic and concrete logic at the same time
 * 
*/ public class MessageWriteSchema implements SchemaEx { protected final ProtoMapper protoMapper; protected final Message message; private final JavaType javaType; // mostly, one message only relate to one pojo private final Class mainPojoCls; private FieldMapEx mainPojoFieldMaps; private FieldMapEx> mapFieldMaps; // if not equals to mainPojoCls, then will find from pojoFieldMaps private final Map, FieldMapEx> pojoFieldMaps = new ConcurrentHashMapEx<>(); @SuppressWarnings("unchecked") public MessageWriteSchema(ProtoMapper protoMapper, Message message, JavaType javaType) { this.protoMapper = protoMapper; this.message = message; this.javaType = javaType; this.mainPojoCls = (Class) javaType.getRawClass(); } public Message getMessage() { return message; } @Override public T newMessage() { throw new UnsupportedOperationException(); } @Override public String messageName() { return message.getName(); } public JavaType getJavaType() { return javaType; } public Class getMainPojoCls() { return mainPojoCls; } public FieldMapEx getMainPojoFieldMaps() { return mainPojoFieldMaps; } @Override public void init() { this.mapFieldMaps = protoMapper.getSerializerSchemaManager().createMapFields(message); if (ProtoUtils.isWrapProperty(message)) { this.mainPojoFieldMaps = createPropertyWrapperFields(javaType); return; } this.mainPojoFieldMaps = createPojoFields(javaType); } private FieldMapEx createPropertyWrapperFields(JavaType javaType) { Field protoField = message.getField(1); PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); propertyDescriptor.setName(protoField.getName()); propertyDescriptor.setJavaType(javaType); FieldSchema fieldSchema = protoMapper.getSerializerSchemaManager() .createSchemaField(protoField, propertyDescriptor); return FieldMapEx.createFieldMap(Arrays.asList(fieldSchema)); } private FieldMapEx createPojoFields(Type type) { SerializerSchemaManager serializerSchemaManager = protoMapper.getSerializerSchemaManager(); BeanDescriptor beanDescriptor = protoMapper.getBeanDescriptorManager().getOrCreateBeanDescriptor(type); List> fieldSchemas = new ArrayList<>(); for (Field protoField : message.getFields()) { PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get(protoField.getName()); if (propertyDescriptor == null) { continue; } Object getter = propertyDescriptor.getGetter(); if (getter == null) { continue; } FieldSchema fieldSchema = serializerSchemaManager.createSchemaField(protoField, propertyDescriptor); fieldSchemas.add(fieldSchema); } return FieldMapEx.createFieldMap(fieldSchemas); } @SuppressWarnings("unchecked") @Override public void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Map) { writeFromMap(output, (Map) value); return; } if (mainPojoCls == value.getClass()) { writeFromMainPojo(output, (T) value); return; } writeDynamicPojo(output, value); } private void writeFromMainPojo(OutputEx output, T value) throws IOException { for (FieldSchema fieldSchema : mainPojoFieldMaps.getFields()) { fieldSchema.getAndWriteTo(output, value); } } @SuppressWarnings("unchecked") private void writeDynamicPojo(OutputEx output, Object dynamicValue) throws IOException { FieldMapEx fieldMapEx = (FieldMapEx) this.pojoFieldMaps .computeIfAbsent(dynamicValue.getClass(), this::createPojoFields); T value = (T) dynamicValue; for (FieldSchema fieldSchema : fieldMapEx.getFields()) { fieldSchema.getAndWriteTo(output, value); } } protected final void writeFromMap(OutputEx output, Map map) throws IOException { for (Entry entry : map.entrySet()) { if (entry.getValue() == null) { continue; } FieldSchema> fieldSchema = mapFieldMaps.getFieldByName(entry.getKey()); if (fieldSchema != null) { fieldSchema.writeTo(output, entry.getValue()); } } } @Override public void mergeFrom(InputEx input, T message) throws IOException { throw new UnsupportedOperationException(); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer; import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isWrapProperty; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.SchemaManager; import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnyEntrySchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnySchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.AnyRepeatedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.BytesRepeatedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.MessagesRepeatedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.PropertyWrapperRepeatedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.StringsRepeatedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools.BoolNotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools.BoolPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles.DoubleNotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles.DoublePackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums.EnumNotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums.EnumPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats.FloatNotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats.FloatPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Fixed32NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Fixed32PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Int32NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Int32PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SFixed32NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SFixed32PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SInt32NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SInt32PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.UInt32NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.UInt32PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Fixed64NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Fixed64PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Int64NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Int64PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SFixed64NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SFixed64PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SInt64NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SInt64PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.UInt64NotPackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.UInt64PackedWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.BoolWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.BytesWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.DoubleWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.EnumWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Fixed32WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Fixed64WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.FloatWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Int32WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Int64WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SFixed32WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SFixed64WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SInt32WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SInt64WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.StringWriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.UInt32WriteSchemas; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.UInt64WriteSchemas; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.protostuff.SchemaEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Message; import io.protostuff.compiler.model.ScalarFieldType; import io.protostuff.runtime.FieldSchema; public class SerializerSchemaManager extends SchemaManager { public SerializerSchemaManager(ProtoMapper protoMapper) { super(protoMapper); } public RootSerializer createRootSerializer(Message message, Type type) { if (ProtoUtils.isAnyMessage(message)) { SchemaEx messageSchema = new AnyEntrySchema(protoMapper, type); return new RootSerializer(messageSchema); } JavaType javaType = TypeFactory.defaultInstance().constructType(type); SchemaEx messageSchema = getOrCreateMessageSchema(message, javaType); return new RootSerializer(messageSchema); } public RootSerializer createRootSerializer(Message message, Map types) { throw new IllegalStateException("not implemented"); } @Override protected SchemaEx newMessageSchema(Message message, JavaType javaType) { return new MessageWriteSchema<>(protoMapper, message, javaType); } @Override protected SchemaEx newMessageSchema(Message message, Map types) { throw new IllegalStateException("not implemented"); } protected FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor) { if (protoField.getType().isEnum()) { return EnumWriteSchemas.create(protoField, propertyDescriptor); } switch ((ScalarFieldType) protoField.getType()) { case INT32: return Int32WriteSchemas.create(protoField, propertyDescriptor); case UINT32: return UInt32WriteSchemas.create(protoField, propertyDescriptor); case SINT32: return SInt32WriteSchemas.create(protoField, propertyDescriptor); case FIXED32: return Fixed32WriteSchemas.create(protoField, propertyDescriptor); case SFIXED32: return SFixed32WriteSchemas.create(protoField, propertyDescriptor); case INT64: return Int64WriteSchemas.create(protoField, propertyDescriptor); case UINT64: return UInt64WriteSchemas.create(protoField, propertyDescriptor); case SINT64: return SInt64WriteSchemas.create(protoField, propertyDescriptor); case FIXED64: return Fixed64WriteSchemas.create(protoField, propertyDescriptor); case SFIXED64: return SFixed64WriteSchemas.create(protoField, propertyDescriptor); case FLOAT: return FloatWriteSchemas.create(protoField, propertyDescriptor); case DOUBLE: return DoubleWriteSchemas.create(protoField, propertyDescriptor); case BOOL: return BoolWriteSchemas.create(protoField, propertyDescriptor); case STRING: return StringWriteSchemas.create(protoField, propertyDescriptor); case BYTES: return BytesWriteSchemas.create(protoField, propertyDescriptor); default: throw new IllegalStateException("unknown proto field type: " + protoField.getType()); } } @Override protected FieldSchema createRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { boolean packed = ProtoUtils.isPacked(protoField); if (protoField.getType().isEnum()) { return packed ? EnumPackedWriteSchemas.create(protoField, propertyDescriptor) : EnumNotPackedWriteSchemas.create(protoField, propertyDescriptor); } if (protoField.getType().isScalar()) { switch ((ScalarFieldType) protoField.getType()) { case INT32: return packed ? Int32PackedWriteSchemas.create(protoField, propertyDescriptor) : Int32NotPackedWriteSchemas.create(protoField, propertyDescriptor); case UINT32: return packed ? UInt32PackedWriteSchemas.create(protoField, propertyDescriptor) : UInt32NotPackedWriteSchemas.create(protoField, propertyDescriptor); case SINT32: return packed ? SInt32PackedWriteSchemas.create(protoField, propertyDescriptor) : SInt32NotPackedWriteSchemas.create(protoField, propertyDescriptor); case FIXED32: return packed ? Fixed32PackedWriteSchemas.create(protoField, propertyDescriptor) : Fixed32NotPackedWriteSchemas.create(protoField, propertyDescriptor); case SFIXED32: return packed ? SFixed32PackedWriteSchemas.create(protoField, propertyDescriptor) : SFixed32NotPackedWriteSchemas.create(protoField, propertyDescriptor); case INT64: return packed ? Int64PackedWriteSchemas.create(protoField, propertyDescriptor) : Int64NotPackedWriteSchemas.create(protoField, propertyDescriptor); case UINT64: return packed ? UInt64PackedWriteSchemas.create(protoField, propertyDescriptor) : UInt64NotPackedWriteSchemas.create(protoField, propertyDescriptor); case SINT64: return packed ? SInt64PackedWriteSchemas.create(protoField, propertyDescriptor) : SInt64NotPackedWriteSchemas.create(protoField, propertyDescriptor); case FIXED64: return packed ? Fixed64PackedWriteSchemas.create(protoField, propertyDescriptor) : Fixed64NotPackedWriteSchemas.create(protoField, propertyDescriptor); case SFIXED64: return packed ? SFixed64PackedWriteSchemas.create(protoField, propertyDescriptor) : SFixed64NotPackedWriteSchemas.create(protoField, propertyDescriptor); case FLOAT: return packed ? FloatPackedWriteSchemas.create(protoField, propertyDescriptor) : FloatNotPackedWriteSchemas.create(protoField, propertyDescriptor); case DOUBLE: return packed ? DoublePackedWriteSchemas.create(protoField, propertyDescriptor) : DoubleNotPackedWriteSchemas.create(protoField, propertyDescriptor); case BOOL: return packed ? BoolPackedWriteSchemas.create(protoField, propertyDescriptor) : BoolNotPackedWriteSchemas.create(protoField, propertyDescriptor); case STRING: return StringsRepeatedWriteSchemas.create(protoField, propertyDescriptor); case BYTES: return BytesRepeatedWriteSchemas.create(protoField, propertyDescriptor); default: ProtoUtils.throwNotSupportWrite(protoField, propertyDescriptor.getJavaType().getRawClass()); } } if (ProtoUtils.isAnyField(protoField)) { FieldSchema anySchema = new AnySchema<>(protoMapper, protoField, propertyDescriptor); return AnyRepeatedWriteSchemas.create(protoField, propertyDescriptor, anySchema); } if (protoField.getType().isMessage()) { JavaType contentType = propertyDescriptor.getJavaType().getContentType(); if (contentType == null) { contentType = ProtoConst.OBJECT_TYPE; } SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); if (isWrapProperty((Message) protoField.getType())) { return PropertyWrapperRepeatedWriteSchemas.create(protoField, propertyDescriptor, contentSchema); } return MessagesRepeatedWriteSchemas.create(protoField, propertyDescriptor, contentSchema); } ProtoUtils.throwNotSupportWrite(protoField, propertyDescriptor.getJavaType().getRawClass()); return null; } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractPrimitiveWriters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated; import static org.apache.servicecomb.foundation.common.utils.ReflectUtils.getFieldArgument; import java.io.IOException; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import io.protostuff.OutputEx; import io.protostuff.SchemaWriter; import io.protostuff.compiler.model.Field; public abstract class AbstractPrimitiveWriters extends AbstractWriters { public SchemaWriter primitiveArrayWriter; public final Class primitiveArrayClass; @SuppressWarnings("unchecked") public AbstractPrimitiveWriters(Field protoField) { super(protoField); this.primitiveArrayClass = getFieldArgument(this.getClass(), "primitiveArrayClass"); } @SuppressWarnings("unchecked") public final void dynamicWriteTo(OutputEx output, Object value) throws IOException { // from normal model if (primitiveArrayClass.isInstance(value)) { primitiveArrayWriter.writeTo(output, (PRIMITIVE_ARRAY) value); return; } if (arrayClass.isInstance(value)) { arrayWriter.writeTo(output, (PRIMITIVE_WRAPPER[]) value); return; } if (value instanceof Collection) { collectionWriter.writeTo(output, (Collection) value); return; } // from http request if (value instanceof String[]) { stringArrayWriter.writeTo(output, (String[]) value); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractWriters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated; import static org.apache.servicecomb.foundation.common.utils.ReflectUtils.getFieldArgument; import java.io.IOException; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import io.protostuff.OutputEx; import io.protostuff.ProtobufOutputEx; import io.protostuff.SchemaWriter; import io.protostuff.WireFormat; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldTypeUtils; public abstract class AbstractWriters { protected final Field protoField; protected final int tag; protected final int tagSize; public SchemaWriter arrayWriter; public SchemaWriter> collectionWriter; public SchemaWriter stringArrayWriter; public final Class arrayClass; @SuppressWarnings("unchecked") public AbstractWriters(Field protoField) { this(protoField, null); } public AbstractWriters(Field protoField, Class arrayClass) { this.protoField = protoField; int wireType = ProtoUtils.isPacked(protoField) && protoField.isRepeated() ? WireFormat.WIRETYPE_LENGTH_DELIMITED : FieldTypeUtils.convert(protoField.getType()).wireType; this.tag = WireFormat.makeTag(protoField.getTag(), wireType); this.tagSize = ProtobufOutputEx.computeRawVarint32Size(tag); if (arrayClass == null) { arrayClass = getFieldArgument(this.getClass(), "arrayWriter"); } this.arrayClass = arrayClass; } @SuppressWarnings("unchecked") public void dynamicWriteTo(OutputEx output, Object value) throws IOException { if (value instanceof Collection) { collectionWriter.writeTo(output, (Collection) value); return; } // from normal model if (arrayClass.isInstance(value)) { arrayWriter.writeTo(output, (T[]) value); return; } // from http request if (value instanceof String[]) { stringArrayWriter.writeTo(output, (String[]) value); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedPrimitiveWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas.DynamicSchema; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class RepeatedPrimitiveWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveWriters writers) { JavaType javaType = propertyDescriptor.getJavaType(); if (writers.primitiveArrayClass == javaType.getRawClass()) { return new PrimitiveArraySchema<>(protoField, propertyDescriptor, writers); } return RepeatedWriteSchemas.create(protoField, propertyDescriptor, writers); } private static class PrimitiveArraySchema extends DynamicSchema { private final Getter getter; private final AbstractPrimitiveWriters primitiveWriters; public PrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractPrimitiveWriters writers) { super(protoField, propertyDescriptor, writers); this.getter = propertyDescriptor.getGetter(); this.primitiveWriters = writers; } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { PRIMITIVE_ARRAY value = getter.get(message); if (value == null) { return; } primitiveWriters.primitiveArrayWriter.writeTo(output, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class RepeatedWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, AbstractWriters writers) { return new CollectionSchema<>(protoField, propertyDescriptor, writers); } static class DynamicSchema extends FieldSchema { protected final AbstractWriters writers; @SuppressWarnings("unchecked") public DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractWriters writers) { super(protoField, propertyDescriptor.getJavaType()); this.writers = writers; } @SuppressWarnings("unchecked") @Override public final void writeTo(OutputEx output, Object value) throws IOException { writers.dynamicWriteTo(output, value); } } private static class CollectionSchema extends DynamicSchema { private final Getter getter; public CollectionSchema(Field protoField, PropertyDescriptor propertyDescriptor, AbstractWriters writers) { super(protoField, propertyDescriptor, writers); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value == null) { return; } this.writeTo(output, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/AnyRepeatedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class AnyRepeatedWriteSchemas { private static class AnyWriters extends AbstractWriters { public AnyWriters(Field protoField, FieldSchema anySchema) { super(protoField); arrayWriter = (output, array) -> { for (Object element : array) { if (element != null) { anySchema.writeTo(output, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (output, collection) -> { for (Object element : collection) { if (element != null) { anySchema.writeTo(output, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } @SuppressWarnings("unchecked") public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, FieldSchema anySchema) { return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new AnyWriters<>(protoField, anySchema)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/BytesRepeatedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BytesRepeatedWriteSchemas { private static class BytesWriters extends AbstractWriters { public BytesWriters(Field protoField) { super(protoField); arrayWriter = (output, array) -> { for (byte[] element : array) { if (element != null) { output.writeByteArray(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (output, collection) -> { for (byte[] element : collection) { if (element != null) { output.writeByteArray(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } @SuppressWarnings("unchecked") public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new BytesWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/MessagesRepeatedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl; import java.lang.reflect.Array; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.MessageWriteSchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import io.protostuff.SchemaWriter; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class MessagesRepeatedWriteSchemas { private static class MessageWriters extends AbstractWriters { @SuppressWarnings("unchecked") public MessageWriters(Field protoField, SchemaWriter elementSchema) { super(protoField, (Class) Array.newInstance(((MessageWriteSchema) elementSchema).getMainPojoCls(), 0) .getClass()); arrayWriter = (output, array) -> { for (Object element : array) { if (element != null) { output.writeObject(tag, tagSize, element, elementSchema); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (output, collection) -> { for (Object element : collection) { if (element != null) { output.writeObject(tag, tagSize, element, elementSchema); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } @SuppressWarnings("unchecked") public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, SchemaWriter elementSchema) { return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new MessageWriters(protoField, elementSchema)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/PropertyWrapperRepeatedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl; import java.lang.reflect.Array; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.MessageWriteSchema; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.SchemaWriter; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class PropertyWrapperRepeatedWriteSchemas { private static class PropertyWrapperWriters extends AbstractWriters { public PropertyWrapperWriters(Field protoField, SchemaWriter elementSchema, Class arrayClass) { super(protoField, arrayClass); FieldSchema fieldSchema = (elementSchema instanceof MessageWriteSchema) ? ((MessageWriteSchema) elementSchema) .getMainPojoFieldMaps() .getFieldByNumber(1) : null; arrayWriter = (output, array) -> { for (Object element : array) { if (element != null) { output.writeObject(tag, tagSize, element, fieldSchema::writeTo); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (output, collection) -> { for (Object element : collection) { if (element != null) { output.writeObject(tag, tagSize, element, fieldSchema::writeTo); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } @SuppressWarnings("unchecked") public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, SchemaWriter elementSchema) { JavaType contentType = propertyDescriptor.getJavaType().getContentType(); Class contentClass = contentType == null ? Object.class : (Class) contentType.getRawClass(); Class arrayClass = (Class) Array.newInstance(contentClass, 0).getClass(); return RepeatedWriteSchemas .create(protoField, propertyDescriptor, new PropertyWrapperWriters(protoField, elementSchema, arrayClass)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/StringsRepeatedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class StringsRepeatedWriteSchemas { private static class StringWriters extends AbstractWriters { public StringWriters(Field protoField) { super(protoField); arrayWriter = (output, array) -> { for (String element : array) { if (element != null) { output.writeString(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (output, collection) -> { for (String element : collection) { if (element != null) { output.writeString(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } @SuppressWarnings("unchecked") public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new StringWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolNotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolNotPackedWriteSchemas { private static class BoolNotPackedWriters extends AbstractPrimitiveWriters { public BoolNotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, boolean[] array) -> { for (boolean element : array) { output.writeBool(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Boolean[] array) -> { for (Boolean element : array) { if (element != null) { output.writeBool(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Boolean element : collection) { if (element != null) { output.writeBool(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { boolean parsedValue = Boolean.parseBoolean(element); output.writeBool(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new BoolNotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolPackedWriteSchemas { private static class BoolPackedWriters extends AbstractPrimitiveWriters { public BoolPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (boolean element : array) { output.writePackedBool(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Boolean element : array) { if (element != null) { output.writePackedBool(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Boolean element : collection) { if (element != null) { output.writePackedBool(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { boolean parsedValue = Boolean.parseBoolean(element); output.writePackedBool(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new BoolPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoubleNotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoubleNotPackedWriteSchemas { private static class DoubleNotPackedWriters extends AbstractPrimitiveWriters { public DoubleNotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, double[] array) -> { for (double element : array) { output.writeDouble(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Double[] array) -> { for (Double element : array) { if (element != null) { output.writeDouble(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Double element : collection) { if (element != null) { output.writeDouble(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { double parsedValue = Double.parseDouble(element); output.writeDouble(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new DoubleNotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoublePackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoublePackedWriteSchemas { private static class DoublePackedWriters extends AbstractPrimitiveWriters { public DoublePackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (double element : array) { output.writePackedDouble(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Double element : array) { if (element != null) { output.writePackedDouble(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Double element : collection) { if (element != null) { output.writePackedDouble(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { double parsedValue = Double.parseDouble(element); output.writePackedDouble(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new DoublePackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumNotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums; import java.io.IOException; import java.util.Collection; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import io.protostuff.runtime.FieldSchema; public class EnumNotPackedWriteSchemas { private static class EnumNotPackedWriters extends AbstractWriters> { private final EnumMeta enumMeta; @SuppressWarnings("unchecked") public EnumNotPackedWriters(Field protoField, JavaType javaType) { super(protoField, ReflectUtils.constructArrayType(Enum.class)); this.enumMeta = new EnumMeta(protoField, javaType); arrayWriter = (output, array) -> { for (Enum element : array) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } String name = element.name(); Integer enumValue = enumMeta.getValueByName(name); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", name, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writeEnum(tag, tagSize, enumValue); } }; collectionWriter = (output, collection) -> { if (collection.isEmpty()) { return; } Object first = collection.iterator().next(); if (first.getClass().isEnum()) { writeEnumCollection(output, collection); return; } if (first.getClass() == String.class) { writeStringCollection(output, (Collection) (Object) collection); return; } writeIntCollection(output, (Collection) (Object) collection); }; stringArrayWriter = (output, array) -> { for (String element : array) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } Integer enumValue = enumMeta.getValueByName(element); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", element, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writeEnum(tag, tagSize, enumValue); } }; } private void writeStringCollection(OutputEx output, Collection collection) throws IOException { for (String element : collection) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } Integer enumValue = enumMeta.getValueByName(element); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", element, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writeEnum(tag, tagSize, enumValue); } } private void writeIntCollection(OutputEx output, Collection collection) throws IOException { for (Number element : collection) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } output.writeInt32(tag, tagSize, element.intValue()); } } private void writeEnumCollection(OutputEx output, Collection> collection) throws IOException { for (Enum element : collection) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } String name = element.name(); Integer enumValue = enumMeta.getValueByName(name); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", name, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writeEnum(tag, tagSize, enumValue); } } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new EnumNotPackedWriters(protoField, propertyDescriptor.getJavaType())); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums; import java.io.IOException; import java.util.Collection; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; import com.fasterxml.jackson.databind.JavaType; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import io.protostuff.runtime.FieldSchema; public class EnumPackedWriteSchemas { private static class EnumsPackedWriters extends AbstractWriters> { private final EnumMeta enumMeta; @SuppressWarnings("unchecked") public EnumsPackedWriters(Field protoField, JavaType javaType) { super(protoField, ReflectUtils.constructArrayType(Enum.class)); this.enumMeta = new EnumMeta(protoField, javaType); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Enum element : array) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } String name = element.name(); Integer enumValue = enumMeta.getValueByName(name); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", name, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writePackedEnum(enumValue); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { Object first = collection.iterator().next(); if (first.getClass().isEnum()) { writeEnumCollection(output, collection); return; } if (first.getClass() == String.class) { writeStringCollection(output, (Collection) (Object) collection); return; } writeIntCollection(output, (Collection) (Object) collection); }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } Integer enumValue = enumMeta.getValueByName(element); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", element, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writePackedEnum(enumValue); } }); } private void writeStringCollection(OutputEx output, Collection collection) throws IOException { for (String element : collection) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } Integer enumValue = enumMeta.getValueByName(element); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", element, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writePackedEnum(enumValue); } } private void writeIntCollection(OutputEx output, Collection collection) throws IOException { for (Number element : collection) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } output.writePackedInt32(element.intValue()); } } private void writeEnumCollection(OutputEx output, Collection> collection) throws IOException { for (Enum element : collection) { if (element == null) { ProtoUtils.throwNotSupportNullElement(protoField); return; } String name = element.name(); Integer enumValue = enumMeta.getValueByName(name); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", name, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writePackedEnum(enumValue); } } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedWriteSchemas .create(protoField, propertyDescriptor, new EnumsPackedWriters(protoField, propertyDescriptor.getJavaType())); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatNotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatNotPackedWriteSchemas { private static class FloatNotPackedWriters extends AbstractPrimitiveWriters { public FloatNotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, float[] array) -> { for (float element : array) { output.writeFloat(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Float[] array) -> { for (Float element : array) { if (element != null) { output.writeFloat(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Float element : collection) { if (element != null) { output.writeFloat(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { float parsedValue = Float.parseFloat(element); output.writeFloat(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new FloatNotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatPackedWriteSchemas { private static class FloatPackedWriters extends AbstractPrimitiveWriters { public FloatPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (float element : array) { output.writePackedFloat(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Float element : array) { if (element != null) { output.writePackedFloat(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Float element : collection) { if (element != null) { output.writePackedFloat(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { float parsedValue = Float.parseFloat(element); output.writePackedFloat(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new FloatPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed32NotPackedWriteSchemas { private static class Fixed32NotPackedWriters extends AbstractPrimitiveWriters { public Fixed32NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, int[] array) -> { for (int element : array) { output.writeFixed32(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Integer[] array) -> { for (Integer element : array) { if (element != null) { output.writeFixed32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Integer element : collection) { if (element != null) { output.writeFixed32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writeFixed32(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas .create(protoField, propertyDescriptor, new Fixed32NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed32PackedWriteSchemas { private static class Fixed32PackedWriters extends AbstractPrimitiveWriters { public Fixed32PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (int element : array) { output.writePackedFixed32(element); } }); }; arrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (Integer element : array) { if (element != null) { output.writePackedFixed32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; collectionWriter = (o, value) -> { if (value.isEmpty()) { return; } o.writeObject(tag, tagSize, value, (output, collection) -> { for (Integer element : collection) { if (element != null) { output.writePackedFixed32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; stringArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writePackedFixed32(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Fixed32PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int32NotPackedWriteSchemas { private static class Int32NotPackedWriters extends AbstractPrimitiveWriters { public Int32NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, int[] array) -> { for (int element : array) { output.writeInt32(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Integer[] array) -> { for (Integer element : array) { if (element != null) { output.writeInt32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Integer element : collection) { if (element != null) { output.writeInt32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writeInt32(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int32NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int32PackedWriteSchemas { private static class Int32PackedWriters extends AbstractPrimitiveWriters { public Int32PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (int element : array) { output.writePackedInt32(element); } }); }; arrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (Integer element : array) { if (element != null) { output.writePackedInt32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; collectionWriter = (o, value) -> { if (value.isEmpty()) { return; } o.writeObject(tag, tagSize, value, (output, collection) -> { for (Integer element : collection) { if (element != null) { output.writePackedInt32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; stringArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writePackedInt32(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int32PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed32NotPackedWriteSchemas { private static class SFixed32NotPackedWriters extends AbstractPrimitiveWriters { public SFixed32NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, int[] array) -> { for (int element : array) { output.writeSFixed32(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Integer[] array) -> { for (Integer element : array) { if (element != null) { output.writeSFixed32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Integer element : collection) { if (element != null) { output.writeSFixed32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writeSFixed32(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas .create(protoField, propertyDescriptor, new SFixed32NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed32PackedWriteSchemas { private static class SFixed32PackedWriters extends AbstractPrimitiveWriters { public SFixed32PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (int element : array) { output.writePackedSFixed32(element); } }); }; arrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (Integer element : array) { if (element != null) { output.writePackedSFixed32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; collectionWriter = (o, value) -> { if (value.isEmpty()) { return; } o.writeObject(tag, tagSize, value, (output, collection) -> { for (Integer element : collection) { if (element != null) { output.writePackedSFixed32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; stringArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writePackedSFixed32(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SFixed32PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt32NotPackedWriteSchemas { private static class SInt32NotPackedWriters extends AbstractPrimitiveWriters { public SInt32NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, int[] array) -> { for (int element : array) { output.writeSInt32(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Integer[] array) -> { for (Integer element : array) { if (element != null) { output.writeSInt32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Integer element : collection) { if (element != null) { output.writeSInt32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writeSInt32(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt32NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt32PackedWriteSchemas { private static class SInt32PackedWriters extends AbstractPrimitiveWriters { public SInt32PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (int element : array) { output.writePackedSInt32(element); } }); }; arrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (Integer element : array) { if (element != null) { output.writePackedSInt32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; collectionWriter = (o, value) -> { if (value.isEmpty()) { return; } o.writeObject(tag, tagSize, value, (output, collection) -> { for (Integer element : collection) { if (element != null) { output.writePackedSInt32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; stringArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writePackedSInt32(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt32PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt32NotPackedWriteSchemas { private static class UInt32NotPackedWriters extends AbstractPrimitiveWriters { public UInt32NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, int[] array) -> { for (int element : array) { output.writeUInt32(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Integer[] array) -> { for (Integer element : array) { if (element != null) { output.writeUInt32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Integer element : collection) { if (element != null) { output.writeUInt32(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writeUInt32(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt32NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt32PackedWriteSchemas { private static class UInt32PackedWriters extends AbstractPrimitiveWriters { public UInt32PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (int element : array) { output.writePackedUInt32(element); } }); }; arrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (Integer element : array) { if (element != null) { output.writePackedUInt32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; collectionWriter = (o, value) -> { if (value.isEmpty()) { return; } o.writeObject(tag, tagSize, value, (output, collection) -> { for (Integer element : collection) { if (element != null) { output.writePackedUInt32(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; stringArrayWriter = (o, value) -> { if (value.length == 0) { return; } o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { int parsedValue = Integer.parseInt(element, 10); output.writePackedUInt32(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt32PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed64NotPackedWriteSchemas { private static class Fixed64NotPackedWriters extends AbstractPrimitiveWriters { public Fixed64NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (output, array) -> { for (long element : array) { output.writeFixed64(tag, tagSize, element); } }; arrayWriter = (output, array) -> { for (Long element : array) { if (element != null) { output.writeFixed64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (output, collection) -> { for (Long element : collection) { if (element != null) { output.writeFixed64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (output, array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writeFixed64(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas .create(protoField, propertyDescriptor, new Fixed64NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed64PackedWriteSchemas { private static class Fixed64PackedWriters extends AbstractPrimitiveWriters { public Fixed64PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (long element : array) { output.writePackedFixed64(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Long element : array) { if (element != null) { output.writePackedFixed64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Long element : collection) { if (element != null) { output.writePackedFixed64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writePackedFixed64(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Fixed64PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int64NotPackedWriteSchemas { private static class Int64NotPackedWriters extends AbstractPrimitiveWriters { public Int64NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, long[] array) -> { for (long element : array) { output.writeInt64(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Long[] array) -> { for (Long element : array) { if (element != null) { output.writeInt64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Long element : collection) { if (element != null) { output.writeInt64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writeInt64(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int64NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int64PackedWriteSchemas { private static class Int64PackedWriters extends AbstractPrimitiveWriters { public Int64PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (long element : array) { output.writePackedInt64(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Long element : array) { if (element != null) { output.writePackedInt64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Long element : collection) { if (element != null) { output.writePackedInt64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writePackedInt64(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int64PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed64NotPackedWriteSchemas { private static class SFixed64NotPackedWriters extends AbstractPrimitiveWriters { public SFixed64NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, long[] array) -> { for (long element : array) { output.writeSFixed64(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Long[] array) -> { for (Long element : array) { if (element != null) { output.writeSFixed64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Long element : collection) { if (element != null) { output.writeSFixed64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writeSFixed64(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas .create(protoField, propertyDescriptor, new SFixed64NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed64PackedWriteSchemas { private static class SFixed64PackedWriters extends AbstractPrimitiveWriters { public SFixed64PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (long element : array) { output.writePackedSFixed64(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Long element : array) { if (element != null) { output.writePackedSFixed64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Long element : collection) { if (element != null) { output.writePackedSFixed64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writePackedSFixed64(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SFixed64PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt64NotPackedWriteSchemas { private static class SInt64NotPackedWriters extends AbstractPrimitiveWriters { public SInt64NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, long[] array) -> { for (long element : array) { output.writeSInt64(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Long[] array) -> { for (Long element : array) { if (element != null) { output.writeSInt64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Long element : collection) { if (element != null) { output.writeSInt64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writeSInt64(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt64NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt64PackedWriteSchemas { private static class SInt64PackedWriters extends AbstractPrimitiveWriters { public SInt64PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (long element : array) { output.writePackedSInt64(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Long element : array) { if (element != null) { output.writePackedSInt64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Long element : collection) { if (element != null) { output.writePackedSInt64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writePackedSInt64(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt64PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64NotPackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import java.util.Collection; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt64NotPackedWriteSchemas { private static class UInt64NotPackedWriters extends AbstractPrimitiveWriters { public UInt64NotPackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (OutputEx output, long[] array) -> { for (long element : array) { output.writeUInt64(tag, tagSize, element); } }; arrayWriter = (OutputEx output, Long[] array) -> { for (Long element : array) { if (element != null) { output.writeUInt64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; collectionWriter = (OutputEx output, Collection collection) -> { for (Long element : collection) { if (element != null) { output.writeUInt64(tag, tagSize, element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; stringArrayWriter = (OutputEx output, String[] array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writeUInt64(tag, tagSize, parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }; } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt64NotPackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64PackedWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractPrimitiveWriters; import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt64PackedWriteSchemas { private static class UInt64PackedWriters extends AbstractPrimitiveWriters { public UInt64PackedWriters(Field protoField) { super(protoField); primitiveArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (long element : array) { output.writePackedUInt64(element); } }); arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (Long element : array) { if (element != null) { output.writePackedUInt64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { for (Long element : collection) { if (element != null) { output.writePackedUInt64(element); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { for (String element : array) { if (element != null) { long parsedValue = Long.parseLong(element, 10); output.writePackedUInt64(parsedValue); continue; } ProtoUtils.throwNotSupportNullElement(protoField); } }); } } public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt64PackedWriters(protoField)); } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BoolWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.BoolGetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BoolWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (boolean.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new BooleanPrimitiveSchema<>(protoField, propertyDescriptor); } if (Boolean.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new BooleanSchema<>(protoField, propertyDescriptor); } return new BooleanSchema<>(protoField, propertyDescriptor); } private static class BooleanDynamicSchema extends FieldSchema { public BooleanDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Boolean) { output.writeScalarBool(tag, tagSize, (boolean) value); return; } if (value instanceof Number) { output.writeScalarBool(tag, tagSize, ((Number) value).longValue() == 1); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } boolean parsedValue = Boolean.parseBoolean(((String[]) value)[0]); output.writeScalarBool(tag, tagSize, parsedValue); return; } if (value instanceof String) { boolean parsedValue = Boolean.parseBoolean((String) value); output.writeScalarBool(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class BooleanSchema extends BooleanDynamicSchema { protected final Getter getter; public BooleanSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class BooleanPrimitiveSchema extends BooleanDynamicSchema { private final BoolGetter primitiveGetter; public BooleanPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { boolean value = primitiveGetter.get(message); output.writeScalarBool(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BytesWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class BytesWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (byte[].class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new BytesSchema<>(protoField, propertyDescriptor); } return new BytesSchema<>(protoField, propertyDescriptor); } private static class BytesDynamicSchema extends FieldSchema { public BytesDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof byte[]) { output.writeByteArray(tag, tagSize, (byte[]) value); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class BytesSchema extends BytesDynamicSchema { protected final Getter getter; public BytesSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/DoubleWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.DoubleGetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class DoubleWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (double.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new DoublePrimitiveSchema<>(protoField, propertyDescriptor); } if (Double.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new DoubleSchema<>(protoField, propertyDescriptor); } return new DoubleSchema<>(protoField, propertyDescriptor); } private static class DoubleDynamicSchema extends FieldSchema { public DoubleDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarDouble(tag, tagSize, ((Number) value).doubleValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } double parsedValue = Double.parseDouble(((String[]) value)[0]); output.writeScalarDouble(tag, tagSize, parsedValue); return; } if (value instanceof String) { double parsedValue = Double.parseDouble((String) value); output.writeScalarDouble(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class DoubleSchema extends DoubleDynamicSchema { protected final Getter getter; public DoubleSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class DoublePrimitiveSchema extends DoubleDynamicSchema { private final DoubleGetter primitiveGetter; public DoublePrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { double value = primitiveGetter.get(message); output.writeScalarDouble(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/EnumWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.base.DynamicEnum; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import io.protostuff.runtime.FieldSchema; public class EnumWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (propertyDescriptor.getJavaType().isEnumType()) { return new EnumSchema<>(protoField, propertyDescriptor); } return new EnumSchema<>(protoField, propertyDescriptor); } private static class EnumDynamicSchema extends FieldSchema { private final EnumMeta enumMeta; public EnumDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); this.enumMeta = new EnumMeta(protoField, javaType); } protected final void numberWrite(OutputEx output, Number value) throws IOException { int enumValue = value.intValue(); if (!enumMeta.containsValue(enumValue)) { throw new IllegalStateException( String.format("invalid enum value %d for proto %s, field=%s:%s", enumValue, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writeScalarInt32(tag, tagSize, enumValue); } protected final void stringWrite(OutputEx output, String enumName) throws IOException { Integer enumValue = enumMeta.getValueByName(enumName); if (enumValue == null) { throw new IllegalStateException( String.format("invalid enum name %s for proto %s, field=%s:%s", enumName, protoField.getTypeName(), ((Type) protoField.getParent()).getCanonicalName(), protoField.getName())); } output.writeScalarInt32(tag, tagSize, enumValue); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Enum) { stringWrite(output, ((Enum) value).name()); return; } if (value instanceof DynamicEnum) { // protobuf can not support unknown enum, because protobuf encode enum as tag value writeTo(output, ((DynamicEnum) value).getValue()); return; } if (value instanceof Number) { // need to check if it is a valid number // because maybe come from http request numberWrite(output, ((Number) value).intValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } stringWrite(output, ((String[]) value)[0]); return; } if (value instanceof String) { stringWrite(output, (String) value); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class EnumSchema extends EnumDynamicSchema { protected final Getter getter; public EnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed32WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed32WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new Fixed32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new Fixed32Schema<>(protoField, propertyDescriptor); } return new Fixed32Schema<>(protoField, propertyDescriptor); } private static class Fixed32DynamicSchema extends FieldSchema { public Fixed32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarFixed32(tag, tagSize, ((Number) value).intValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } int parsedValue = Integer.parseInt(((String[]) value)[0], 10); output.writeScalarFixed32(tag, tagSize, parsedValue); return; } if (value instanceof String) { int parsedValue = Integer.parseInt((String) value, 10); output.writeScalarFixed32(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class Fixed32Schema extends Fixed32DynamicSchema { protected final Getter getter; public Fixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class Fixed32PrimitiveSchema extends Fixed32DynamicSchema { private final IntGetter primitiveGetter; public Fixed32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { int value = primitiveGetter.get(message); output.writeScalarFixed32(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed64WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Fixed64WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new Fixed64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new Fixed64Schema<>(protoField, propertyDescriptor); } return new Fixed64Schema<>(protoField, propertyDescriptor); } private static class Fixed64DynamicSchema extends FieldSchema { public Fixed64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarFixed64(tag, tagSize, ((Number) value).longValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } long parsedValue = Long.parseLong(((String[]) value)[0], 10); output.writeScalarFixed64(tag, tagSize, parsedValue); return; } if (value instanceof String) { long parsedValue = Long.parseLong((String) value, 10); output.writeScalarFixed64(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class Fixed64Schema extends Fixed64DynamicSchema { protected final Getter getter; public Fixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class Fixed64PrimitiveSchema extends Fixed64DynamicSchema { private final LongGetter primitiveGetter; public Fixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { long value = primitiveGetter.get(message); output.writeScalarFixed64(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/FloatWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.FloatGetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class FloatWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (float.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new FloatPrimitiveSchema<>(protoField, propertyDescriptor); } if (Float.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new FloatSchema<>(protoField, propertyDescriptor); } return new FloatSchema<>(protoField, propertyDescriptor); } private static class FloatDynamicSchema extends FieldSchema { public FloatDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarFloat(tag, tagSize, ((Number) value).floatValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } float parsedValue = Float.parseFloat(((String[]) value)[0]); output.writeScalarFloat(tag, tagSize, parsedValue); return; } if (value instanceof String) { float parsedValue = Float.parseFloat((String) value); output.writeScalarFloat(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class FloatSchema extends FloatDynamicSchema { protected final Getter getter; public FloatSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = javaType.isPrimitive() ? null : propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class FloatPrimitiveSchema extends FloatDynamicSchema { private final FloatGetter primitiveGetter; public FloatPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { float value = primitiveGetter.get(message); output.writeScalarFloat(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int32WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.ByteGetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.common.utils.bean.ShortGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public final class Int32WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new IntFieldInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (short.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new ShortFieldInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (byte.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new ByteFieldInt32PrimitiveSchema<>(protoField, propertyDescriptor); } return new Int32Schema<>(protoField, propertyDescriptor); } private static class Int32DynamicSchema extends FieldSchema { public Int32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarInt32(tag, tagSize, ((Number) value).intValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } int parsedValue = Integer.parseInt(((String[]) value)[0], 10); output.writeScalarInt32(tag, tagSize, parsedValue); return; } if (value instanceof String) { if (((String) value).isEmpty()) { return; } int parsedValue = Integer.parseInt((String) value, 10); output.writeScalarInt32(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class Int32Schema extends Int32DynamicSchema { protected final Getter getter; public Int32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static final class IntFieldInt32PrimitiveSchema extends Int32DynamicSchema { private final IntGetter primitiveGetter; public IntFieldInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public void getAndWriteTo(OutputEx output, T message) throws IOException { int value = primitiveGetter.get(message); output.writeScalarInt32(tag, tagSize, value); } } private static final class ShortFieldInt32PrimitiveSchema extends Int32DynamicSchema { private final ShortGetter primitiveGetter; public ShortFieldInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public void getAndWriteTo(OutputEx output, T message) throws IOException { short value = primitiveGetter.get(message); output.writeScalarInt32(tag, tagSize, value); } } private static final class ByteFieldInt32PrimitiveSchema extends Int32DynamicSchema { private final ByteGetter primitiveGetter; public ByteFieldInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public void getAndWriteTo(OutputEx output, T message) throws IOException { byte value = primitiveGetter.get(message); output.writeScalarInt32(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int64WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.temporal.ChronoField; import java.util.Date; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class Int64WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new Int64PrimitiveSchema<>(protoField, propertyDescriptor); } return new Int64Schema<>(protoField, propertyDescriptor); } private static class Int64DynamicSchema extends FieldSchema { public Int64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarInt64(tag, tagSize, ((Number) value).longValue()); return; } if (value instanceof Date) { long parsedValue = ((Date) value).getTime(); output.writeScalarInt64(tag, tagSize, parsedValue); return; } if (value instanceof LocalDate) { long parsedValue = ((LocalDate) value).getLong(ChronoField.EPOCH_DAY); output.writeScalarInt64(tag, tagSize, parsedValue); return; } if (value instanceof LocalDateTime) { long parsedValue = ((LocalDateTime) value).toInstant(ZoneOffset.UTC).toEpochMilli(); output.writeScalarInt64(tag, tagSize, parsedValue); return; } if (value instanceof String) { long parsedValue; if (((String) value).contains(":")) { // from edge, ISO8601 date time, e.g. 2022-05-31T09:16:38.941Z OffsetDateTime offsetDateTime = OffsetDateTime.parse((String) value); parsedValue = offsetDateTime.toInstant().toEpochMilli(); } else { parsedValue = Long.parseLong((String) value, 10); } output.writeScalarInt64(tag, tagSize, parsedValue); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } long parsedValue = Long.parseLong(((String[]) value)[0], 10); output.writeScalarInt64(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class Int64Schema extends Int64DynamicSchema { protected final Getter getter; public Int64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { this.writeTo(output, value); } } } private static final class Int64PrimitiveSchema extends Int64DynamicSchema { private final LongGetter primitiveGetter; public Int64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public void getAndWriteTo(OutputEx output, T message) throws IOException { long value = primitiveGetter.get(message); output.writeScalarInt64(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed32WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed32WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SFixed32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SFixed32Schema<>(protoField, propertyDescriptor); } return new SFixed32Schema<>(protoField, propertyDescriptor); } private static class SFixed32DynamicSchema extends FieldSchema { public SFixed32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarSFixed32(tag, tagSize, ((Number) value).intValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } int parsedValue = Integer.parseInt(((String[]) value)[0], 10); output.writeScalarSFixed32(tag, tagSize, parsedValue); return; } if (value instanceof String) { int parsedValue = Integer.parseInt((String) value, 10); output.writeScalarSFixed32(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class SFixed32Schema extends SFixed32DynamicSchema { protected final Getter getter; public SFixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class SFixed32PrimitiveSchema extends SFixed32DynamicSchema { private final IntGetter primitiveGetter; public SFixed32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { int value = primitiveGetter.get(message); output.writeScalarSFixed32(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed64WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SFixed64WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SFixed64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SFixed64Schema<>(protoField, propertyDescriptor); } return new SFixed64Schema<>(protoField, propertyDescriptor); } private static class SFixed64DynamicSchema extends FieldSchema { public SFixed64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarSFixed64(tag, tagSize, ((Number) value).longValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } long parsedValue = Long.parseLong(((String[]) value)[0], 10); output.writeScalarSFixed64(tag, tagSize, parsedValue); return; } if (value instanceof String) { long parsedValue = Long.parseLong((String) value, 10); output.writeScalarSFixed64(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class SFixed64Schema extends SFixed64DynamicSchema { protected final Getter getter; public SFixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class SFixed64PrimitiveSchema extends SFixed64DynamicSchema { private final LongGetter primitiveGetter; public SFixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { long value = primitiveGetter.get(message); output.writeScalarSFixed64(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt32WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt32WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SInt32Schema<>(protoField, propertyDescriptor); } return new SInt32Schema<>(protoField, propertyDescriptor); } private static class SInt32DynamicSchema extends FieldSchema { public SInt32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarSInt32(tag, tagSize, ((Number) value).intValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } int parsedValue = Integer.parseInt(((String[]) value)[0], 10); output.writeScalarSInt32(tag, tagSize, parsedValue); return; } if (value instanceof String) { int parsedValue = Integer.parseInt((String) value, 10); output.writeScalarSInt32(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class SInt32Schema extends SInt32DynamicSchema { protected final Getter getter; public SInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class SInt32PrimitiveSchema extends SInt32DynamicSchema { private final IntGetter primitiveGetter; public SInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { int value = primitiveGetter.get(message); output.writeScalarSInt32(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt64WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class SInt64WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SInt64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new SInt64Schema<>(protoField, propertyDescriptor); } return new SInt64Schema<>(protoField, propertyDescriptor); } private static class SInt64DynamicSchema extends FieldSchema { public SInt64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarSInt64(tag, tagSize, ((Number) value).longValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } long parsedValue = Long.parseLong(((String[]) value)[0], 10); output.writeScalarSInt64(tag, tagSize, parsedValue); return; } if (value instanceof String) { long parsedValue = Long.parseLong((String) value, 10); output.writeScalarSInt64(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class SInt64Schema extends SInt64DynamicSchema { protected final Getter getter; public SInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class SInt64PrimitiveSchema extends SInt64DynamicSchema { private final LongGetter primitiveGetter; public SInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { long value = primitiveGetter.get(message); output.writeScalarSInt64(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/StringWriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import org.apache.servicecomb.foundation.common.utils.bean.CharGetter; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class StringWriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (char.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new CharFieldStringSchema<>(protoField, propertyDescriptor); } if (String.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new StringSchema<>(protoField, propertyDescriptor); } return new StringSchema<>(protoField, propertyDescriptor); } private static class StringDynamicSchema extends FieldSchema { public StringDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof String) { output.writeScalarString(tag, tagSize, (String) value); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } output.writeScalarString(tag, tagSize, ((String[]) value)[0]); return; } if (value instanceof Character) { output.writeScalarString(tag, tagSize, String.valueOf((char) value)); return; } if (value instanceof BigDecimal || value instanceof BigInteger) { output.writeScalarString(tag, tagSize, value.toString()); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class StringSchema extends StringDynamicSchema { protected final Getter getter; public StringSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class CharFieldStringSchema extends StringDynamicSchema { protected final CharGetter getter; public CharFieldStringSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { char value = getter.get(message); writeTo(output, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt32WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt32WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new UInt32PrimitiveSchema<>(protoField, propertyDescriptor); } if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new UInt32Schema<>(protoField, propertyDescriptor); } return new UInt32Schema<>(protoField, propertyDescriptor); } private static class UInt32DynamicSchema extends FieldSchema { public UInt32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarUInt32(tag, tagSize, ((Number) value).intValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } int parsedValue = Integer.parseInt(((String[]) value)[0], 10); output.writeScalarUInt32(tag, tagSize, parsedValue); return; } if (value instanceof String) { int parsedValue = Integer.parseInt((String) value, 10); output.writeScalarUInt32(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class UInt32Schema extends UInt32DynamicSchema { protected final Getter getter; public UInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class UInt32PrimitiveSchema extends UInt32DynamicSchema { private final IntGetter primitiveGetter; public UInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { int value = primitiveGetter.get(message); output.writeScalarUInt32(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt64WriteSchemas.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; import java.io.IOException; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; import io.protostuff.OutputEx; import io.protostuff.compiler.model.Field; import io.protostuff.runtime.FieldSchema; public class UInt64WriteSchemas { public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new UInt64PrimitiveSchema<>(protoField, propertyDescriptor); } if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { return new UInt64Schema<>(protoField, propertyDescriptor); } return new UInt64Schema<>(protoField, propertyDescriptor); } private static class UInt64DynamicSchema extends FieldSchema { public UInt64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor.getJavaType()); } @Override public final void writeTo(OutputEx output, Object value) throws IOException { if (value instanceof Number) { output.writeScalarUInt64(tag, tagSize, ((Number) value).longValue()); return; } if (value instanceof String[]) { if (((String[]) value).length == 0) { return; } long parsedValue = Long.parseLong(((String[]) value)[0], 10); output.writeScalarUInt64(tag, tagSize, parsedValue); return; } if (value instanceof String) { long parsedValue = Long.parseLong((String) value, 10); output.writeScalarUInt64(tag, tagSize, parsedValue); return; } ProtoUtils.throwNotSupportWrite(protoField, value); } } private static class UInt64Schema extends UInt64DynamicSchema { protected final Getter getter; public UInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); this.getter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { Object value = getter.get(message); if (value != null) { writeTo(output, value); } } } private static class UInt64PrimitiveSchema extends UInt64DynamicSchema { private final LongGetter primitiveGetter; public UInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { super(protoField, propertyDescriptor); primitiveGetter = propertyDescriptor.getGetter(); } @Override public final void getAndWriteTo(OutputEx output, T message) throws IOException { long value = primitiveGetter.get(message); output.writeScalarUInt64(tag, tagSize, value); } } } ================================================ FILE: foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt ================================================ 1.List或List这种结构是PB不支持的 类似的场景,只要PB文件解析出错,说明本schema不支持PB相关的传输:highway/grpc等等 2.反序列化,支持java类型为数组,不一定要求是Collection 3.原子类型的序列化/反序列化,(主要是原子数组?) 3.应该只有文件相关的场景,才是真正的不支持 ------------- 优化: 1.支持用户泛型 2. 不兼容: 1.数组编码规则从protoStuff变成标准的protobuf 不确定: 1.List/List/Map>/Map规则应该也有变化,是否有变化,从protoStuff变成标准protobuf ================================================ FILE: foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto ================================================ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; package google.protobuf; option go_package = "google.golang.org/protobuf/types/known/anypb"; option java_package = "com.google.protobuf"; option java_outer_classname = "AnyProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; // `Any` contains an arbitrary serialized protocol buffer message along with a // URL that describes the type of the serialized message. // // Protobuf library provides support to pack/unpack Any values in the form // of utility functions or additional generated methods of the Any type. // // Example 1: Pack and unpack a message in C++. // // Foo foo = ...; // Any any; // any.PackFrom(foo); // ... // if (any.UnpackTo(&foo)) { // ... // } // // Example 2: Pack and unpack a message in Java. // // Foo foo = ...; // Any any = Any.pack(foo); // ... // if (any.is(Foo.class)) { // foo = any.unpack(Foo.class); // } // // or ... // if (any.isSameTypeAs(Foo.getDefaultInstance())) { // foo = any.unpack(Foo.getDefaultInstance()); // } // // Example 3: Pack and unpack a message in Python. // // foo = Foo(...) // any = Any() // any.Pack(foo) // ... // if any.Is(Foo.DESCRIPTOR): // any.Unpack(foo) // ... // // Example 4: Pack and unpack a message in Go // // foo := &pb.Foo{...} // any, err := anypb.New(foo) // if err != nil { // ... // } // ... // foo := &pb.Foo{} // if err := any.UnmarshalTo(foo); err != nil { // ... // } // // The pack methods provided by protobuf library will by default use // 'type.googleapis.com/full.type.name' as the type URL and the unpack // methods only use the fully qualified type name after the last '/' // in the type URL, for example "foo.bar.com/x/y.z" will yield type // name "y.z". // // JSON // ==== // The JSON representation of an `Any` value uses the regular // representation of the deserialized, embedded message, with an // additional field `@type` which contains the type URL. Example: // // package google.profile; // message Person { // string first_name = 1; // string last_name = 2; // } // // { // "@type": "type.googleapis.com/google.profile.Person", // "firstName": , // "lastName": // } // // If the embedded message type is well-known and has a custom JSON // representation, that representation will be embedded adding a field // `value` which holds the custom JSON in addition to the `@type` // field. Example (for message [google.protobuf.Duration][]): // // { // "@type": "type.googleapis.com/google.protobuf.Duration", // "value": "1.212s" // } // message Any { // A URL/resource name that uniquely identifies the type of the serialized // protocol buffer message. This string must contain at least // one "/" character. The last segment of the URL's path must represent // the fully qualified name of the type (as in // `path/google.protobuf.Duration`). The name should be in a canonical form // (e.g., leading "." is not accepted). // // In practice, teams usually precompile into the binary all types that they // expect it to use in the context of Any. However, for URLs which use the // scheme `http`, `https`, or no scheme, one can optionally set up a type // server that maps type URLs to message definitions as follows: // // * If no scheme is provided, `https` is assumed. // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the // URL, or have them precompiled into a binary to avoid any // lookup. Therefore, binary compatibility needs to be preserved // on changes to types. (Use versioned type names to manage // breaking changes.) // // Note: this functionality is not currently available in the official // protobuf release, and it is not used for type URLs beginning with // type.googleapis.com. // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. // string type_url = 1; // Must be a valid serialized protocol buffer of the above specified type. bytes value = 2; } ================================================ FILE: foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto ================================================ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; package google.protobuf; option go_package = "google.golang.org/protobuf/types/known/emptypb"; option java_package = "com.google.protobuf"; option java_outer_classname = "EmptyProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option cc_enable_arenas = true; // A generic empty message that you can re-use to avoid defining duplicated // empty messages in your APIs. A typical example is to use it as the request // or the response type of an API method. For instance: // // service Foo { // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); // } // message Empty {} ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/TestISODateTimeParsing.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf; import java.text.SimpleDateFormat; import java.time.OffsetDateTime; import java.util.Date; import java.util.TimeZone; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestISODateTimeParsing { @Test public void testParseStringToDate() { SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD'T'hh:mm:ssZ"); sdf.setTimeZone(TimeZone.getTimeZone("EST")); OffsetDateTime offsetDateTime = OffsetDateTime.parse("2022-05-31T09:16:38.941Z"); Date d = Date.from(offsetDateTime.toInstant()); String date = sdf.format(d); Assertions.assertEquals("2022-05-151T04:16:38-0500", date); offsetDateTime = OffsetDateTime.parse("2022-05-31T09:16:38.941+00:00"); d = Date.from(offsetDateTime.toInstant()); date = sdf.format(d); Assertions.assertEquals("2022-05-151T04:16:38-0500", date); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/compatibility/TestCompatibilityOfImplementations.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.compatibility; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; import org.apache.servicecomb.foundation.protobuf.performance.engine.Protobuf; import org.apache.servicecomb.foundation.protobuf.performance.engine.ScbWeak; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCompatibilityOfImplementations { static final ProtubufCodecEngine scbWeak = new ScbWeak(); static final ProtubufCodecEngine protobuf = new Protobuf(); @Test @SuppressWarnings("unchecked") public void testEmptyCollection() throws Exception { ProtobufRoot.Root.Builder builder = ProtobufRoot.Root.newBuilder(); byte[] values = protobuf.serialize(builder); Assertions.assertEquals(values.length, 0); ProtobufRoot.Root.Builder o = (ProtobufRoot.Root.Builder) protobuf.deserialize(values); Assertions.assertTrue(o.getFixed32SNotPackedList().isEmpty()); builder = ProtobufRoot.Root.newBuilder().addFixed32SNotPacked(30); values = protobuf.serialize(builder); Assertions.assertArrayEquals(new byte[] {(byte) -123, (byte) 6, (byte) 30, (byte) 0, (byte) 0, (byte) 0}, values); o = (ProtobufRoot.Root.Builder) protobuf.deserialize(values); Assertions.assertEquals(30, (int) o.getFixed32SNotPackedList().get(0)); Root root = new Root(); root.setFixed32sNotPacked(new ArrayList<>()); values = scbWeak.serialize(root); Assertions.assertEquals(values.length, 0); Map newRootMap = (Map) scbWeak.deserialize(values); Assertions.assertNull(newRootMap.get("fixed32sNotPacked")); // This is different , because depends on default model initializer List iValues = new ArrayList<>(); iValues.add(30); root.setFixed32sNotPacked(iValues); values = scbWeak.serialize(root); Assertions.assertArrayEquals(new byte[] {(byte) -123, (byte) 6, (byte) 30, (byte) 0, (byte) 0, (byte) 0}, values); newRootMap = (Map) scbWeak.deserialize(values); Assertions.assertEquals(30, (int) ((List) newRootMap.get("fixed32sNotPacked")).get(0)); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestModelWrap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal; import java.io.IOException; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import io.vertx.core.json.Json; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestModelWrap { protected static ProtoMapperFactory factory = new ProtoMapperFactory(); protected static ProtoMapper modelProtoMapper = factory.createFromName("model.proto"); public static class User { public String name; public User(String name) { this.name = name; } } static User user = new User("uName"); public static class PojoModel { public List> listListUser; public List> listMapUser; public Map> mapListUser; public Map> mapMapUser; public List>> listListListUser; public List>> listListMapUser; public List>> listMapListUser; public List>> listMapMapUser; public Map>> mapListListUser; public Map>> mapListMapUser; public Map>> mapMapListUser; public Map>> mapMapMapUser; public void init() { List listUser = Arrays.asList(user, user); Map mapUser = new LinkedHashMap<>(); mapUser.put("k1", user); mapUser.put("k2", user); listListUser = Arrays.asList(listUser, listUser); listMapUser = Arrays.asList(mapUser, mapUser); mapListUser = new LinkedHashMap<>(); mapListUser.put("k1", listUser); mapListUser.put("k2", listUser); mapMapUser = new LinkedHashMap<>(); mapMapUser.put("k1", mapUser); mapMapUser.put("k2", mapUser); listListListUser = Arrays.asList(listListUser, listListUser); listListMapUser = Arrays.asList(listMapUser, listMapUser); listMapListUser = Arrays.asList(mapListUser, mapListUser); listMapMapUser = Arrays.asList(mapMapUser, mapMapUser); mapListListUser = new LinkedHashMap<>(); mapListListUser.put("k1", listListUser); mapListListUser.put("k2", listListUser); mapListMapUser = new LinkedHashMap<>(); mapListMapUser.put("k1", listMapUser); mapListMapUser.put("k2", listMapUser); mapMapListUser = new LinkedHashMap<>(); mapMapListUser.put("k1", mapListUser); mapMapListUser.put("k2", mapListUser); mapMapMapUser = new LinkedHashMap<>(); mapMapMapUser.put("k1", mapMapUser); mapMapMapUser.put("k2", mapMapUser); } } public static class ProtoModel { public List listListUser; public List listMapUser; public Map mapListUser; public Map mapMapUser; public List listListListUser; public List listListMapUser; public List listMapListUser; public List listMapMapUser; public Map mapListListUser; public Map mapListMapUser; public Map mapMapListUser; public Map mapMapMapUser; public void init() { ProtoListUser protoListUser = new ProtoListUser(); protoListUser.init(); ProtoMapUser protoMapUser = new ProtoMapUser(); protoMapUser.init(); ProtoListListUser protoListListUser = new ProtoListListUser(); protoListListUser.init(); ProtoListMapUser protoListMapUser = new ProtoListMapUser(); protoListMapUser.init(); ProtoMapListUser protoMapListUser = new ProtoMapListUser(); protoMapListUser.init(); ProtoMapMapUser protoMapMapUser = new ProtoMapMapUser(); protoMapMapUser.init(); listListUser = Arrays.asList(protoListUser, protoListUser); listMapUser = Arrays.asList(protoMapUser, protoMapUser); mapListUser = new LinkedHashMap<>(); mapListUser.put("k1", protoListUser); mapListUser.put("k2", protoListUser); mapMapUser = new LinkedHashMap<>(); mapMapUser.put("k1", protoMapUser); mapMapUser.put("k2", protoMapUser); listListListUser = Arrays.asList(protoListListUser, protoListListUser); listListMapUser = Arrays.asList(protoListMapUser, protoListMapUser); listMapListUser = Arrays.asList(protoMapListUser, protoMapListUser); listMapMapUser = Arrays.asList(protoMapMapUser, protoMapMapUser); mapListListUser = new LinkedHashMap<>(); mapListListUser.put("k1", protoListListUser); mapListListUser.put("k2", protoListListUser); mapListMapUser = new LinkedHashMap<>(); mapListMapUser.put("k1", protoListMapUser); mapListMapUser.put("k2", protoListMapUser); mapMapListUser = new LinkedHashMap<>(); mapMapListUser.put("k1", protoMapListUser); mapMapListUser.put("k2", protoMapListUser); mapMapMapUser = new LinkedHashMap<>(); mapMapMapUser.put("k1", protoMapMapUser); mapMapMapUser.put("k2", protoMapMapUser); } } public static class ProtoListListUser { public List value; public void init() { ProtoListUser protoListUser = new ProtoListUser(); protoListUser.init(); value = Arrays.asList(protoListUser, protoListUser); } } public static class ProtoListMapUser { public List value; public void init() { ProtoMapUser protoMapUser = new ProtoMapUser(); protoMapUser.init(); value = Arrays.asList(protoMapUser, protoMapUser); } } public static class ProtoMapListUser { public Map value; public void init() { ProtoListUser protoListUser = new ProtoListUser(); protoListUser.init(); value = new LinkedHashMap<>(); value.put("k1", protoListUser); value.put("k2", protoListUser); } } public static class ProtoMapMapUser { public Map value; public void init() { ProtoMapUser protoMapUser = new ProtoMapUser(); protoMapUser.init(); value = new LinkedHashMap<>(); value.put("k1", protoMapUser); value.put("k2", protoMapUser); } } public static class ProtoListUser { public List value; public void init() { value = Arrays.asList(user, user); } } public static class ProtoMapUser { public Map value; public void init() { value = new LinkedHashMap<>(); value.put("k1", user); value.put("k2", user); } } @SuppressWarnings("unchecked") @Test public void pojoModel() throws IOException { RootSerializer pojoSerializer = modelProtoMapper.createRootSerializer("PojoModel", PojoModel.class); RootDeserializer> pojoMapDeserializer = modelProtoMapper .createRootDeserializer("PojoModel", Map.class); RootDeserializer pojoModelDeserializer = modelProtoMapper .createRootDeserializer("PojoModel", PojoModel.class); RootSerializer protoSerializer = modelProtoMapper.createRootSerializer("ProtoModel", ProtoModel.class); RootDeserializer> protoMapDeserializer = modelProtoMapper .createRootDeserializer("ProtoModel", Map.class); RootDeserializer protoModelDeserializer = modelProtoMapper .createRootDeserializer("ProtoModel", ProtoModel.class); PojoModel pojoModel = new PojoModel(); pojoModel.init(); String jsonPojoModel = Json.encode(pojoModel); Map mapFromPojoModel = (Map) Json.decodeValue(jsonPojoModel, Map.class); ProtoModel protoModel = new ProtoModel(); protoModel.init(); String jsonProtoModel = Json.encode(protoModel); Map mapFromProtoModel = (Map) Json.decodeValue(jsonProtoModel, Map.class); // serialize byte[] bytes = protoSerializer.serialize(protoModel); Assertions.assertArrayEquals(bytes, protoSerializer.serialize(mapFromProtoModel)); Assertions.assertArrayEquals(bytes, pojoSerializer.serialize(pojoModel)); Assertions.assertArrayEquals(bytes, pojoSerializer.serialize(mapFromPojoModel)); // deserialize pojoModel PojoModel newPojoModel = pojoModelDeserializer.deserialize(bytes); Assertions.assertEquals(jsonPojoModel, Json.encode(newPojoModel)); Map mapFromNewPojoModel = pojoMapDeserializer.deserialize(bytes); Assertions.assertEquals(jsonPojoModel, Json.encode(mapFromNewPojoModel)); // deserialize protoModel ProtoModel newProtoModel = protoModelDeserializer.deserialize(bytes); Assertions.assertEquals(jsonProtoModel, Json.encode(newProtoModel)); Map mapFromNewProtoModel = protoMapDeserializer.deserialize(bytes); Assertions.assertEquals(jsonProtoModel, Json.encode(mapFromNewProtoModel)); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestSchemaBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal; import java.io.IOException; import java.util.Map; import org.apache.commons.codec.binary.Hex; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.model.PrimitiveArrays; import org.apache.servicecomb.foundation.protobuf.internal.model.PrimitiveWrapperArrays; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; import io.protostuff.compiler.model.Field; import org.junit.jupiter.api.Assertions; public class TestSchemaBase { protected static ProtoMapperFactory factory = new ProtoMapperFactory(); protected static ProtoMapper protoMapper = factory.createFromName("protobufRoot.proto"); protected static RootSerializer rootSerializer = protoMapper.createRootSerializer("Root", Root.class); protected static RootDeserializer rootDeserializer = protoMapper.createRootDeserializer("Root", Root.class); protected static RootSerializer primitiveArraysSerializer = protoMapper .createRootSerializer("Root", PrimitiveArrays.class); protected static RootDeserializer primitiveArraysDeserializer = protoMapper .createRootDeserializer("Root", PrimitiveArrays.class); protected static RootSerializer primitiveWrapperArraysSerializer = protoMapper .createRootSerializer("Root", PrimitiveWrapperArrays.class); protected static RootDeserializer primitiveWrapperArraysDeserializer = protoMapper .createRootDeserializer("Root", PrimitiveWrapperArrays.class); protected static RootDeserializer> mapRootDeserializer = protoMapper .createRootDeserializer("Root", Map.class); protected Object scbRoot; protected byte[] scbRootBytes; protected Map scbMap; protected byte[] scbMapBytes; protected ProtobufRoot.Root.Builder builder = ProtobufRoot.Root.newBuilder(); protected byte[] protobufBytes; protected String primitiveFieldName; protected Field primitiveProtoField; protected String fieldName; protected Field protoField; protected void initFields(String primitiveFieldName, String fieldName) { this.primitiveFieldName = primitiveFieldName; if (primitiveFieldName != null) { primitiveProtoField = protoMapper.getProto().getMessage("Root").getField(primitiveFieldName); } initField(fieldName); } protected void initField(String fieldName) { this.fieldName = fieldName; if (fieldName != null) { protoField = protoMapper.getProto().getMessage("Root").getField(fieldName); } } protected void checkRepeatedWithPrimitive() throws Exception { check(); check(primitiveArraysDeserializer, mapRootDeserializer, primitiveArraysSerializer, false); String primitiveFieldName = scbMap.keySet().iterator().next(); java.lang.reflect.Field primitiveField = PrimitiveArrays.class.getDeclaredField(primitiveFieldName); primitiveField.setAccessible(true); Object primitiveArray = primitiveField.get(scbRoot); check(primitiveWrapperArraysDeserializer, mapRootDeserializer, primitiveWrapperArraysSerializer, false); String wrapperFieldName = scbMap.keySet().iterator().next(); java.lang.reflect.Field wrapperField = PrimitiveWrapperArrays.class.getDeclaredField(wrapperFieldName); wrapperField.setAccessible(true); Object[] array = (Object[]) wrapperField.get(scbRoot); // dynamic strings String[] strings = new String[array.length]; for (int idx = 0; idx < array.length; idx++) { strings[idx] = array[idx].toString(); } scbMap.clear(); scbMap.put(primitiveFieldName, strings); scbMapBytes = primitiveArraysSerializer.serialize(scbMap); Assertions.assertArrayEquals(protobufBytes, scbMapBytes); // dynamic primitive array scbMap.clear(); scbMap.put(primitiveFieldName, primitiveArray); scbMapBytes = primitiveArraysSerializer.serialize(scbMap); Assertions.assertArrayEquals(protobufBytes, scbMapBytes); // dynamic array scbMap.clear(); scbMap.put(primitiveFieldName, array); scbMapBytes = primitiveArraysSerializer.serialize(scbMap); Assertions.assertArrayEquals(protobufBytes, scbMapBytes); } protected void check() throws IOException { check(rootDeserializer, mapRootDeserializer, rootSerializer, false); } protected void check(boolean print) throws IOException { check(rootDeserializer, mapRootDeserializer, rootSerializer, print); } protected void check(RootDeserializer deserializer, RootDeserializer> mapDeserializer, RootSerializer serializer, boolean print) throws IOException { // 1.standard protobuf serialize to bytes protobufBytes = builder.build().toByteArray(); // 2.weak type deserialize scbMap = mapDeserializer.deserialize(protobufBytes); // 3.weak type serialize scbMapBytes = serializer.serialize(scbMap); // 4.strong type deserialize scbRoot = deserializer.deserialize(scbMapBytes); // 5.strong type serialize scbRootBytes = serializer.serialize(scbRoot); if (print) { System.out.println("scbRoot bytes:" + Hex.encodeHexString(scbRootBytes)); System.out.println("scbRoot len :" + scbRootBytes.length); System.out.println("scbMap bytes :" + Hex.encodeHexString(scbMapBytes)); System.out.println("scbMap len :" + scbMapBytes.length); System.out.println("protobuf :" + Hex.encodeHexString(protobufBytes)); System.out.println("protobuf len :" + protobufBytes.length); } Assertions.assertArrayEquals(protobufBytes, scbMapBytes); Assertions.assertArrayEquals(protobufBytes, scbRootBytes); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/bean/TestBeanDescriptorManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.bean; import java.lang.reflect.Method; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBeanDescriptorManager { public static class Model { private int both; private int onlyGet; private int onlySet; public int direct; public int getBoth() { return both; } public void setBoth(int both) { this.both = both; } public int getOnlyGet() { return onlyGet; } public void onlyGet(int value) { this.onlyGet = value; } public void setOnlySet(int onlySet) { this.onlySet = onlySet; } public int onlySet() { return onlySet; } } public static class CustomGeneric { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } } public static class GenericSchema { public CustomGeneric genericMethod(CustomGeneric input) { return null; } } static ObjectMapper mapper = new ObjectMapper(); static BeanDescriptorManager beanDescriptorManager = new BeanDescriptorManager(mapper.getSerializationConfig()); static BeanDescriptor beanDescriptor = beanDescriptorManager.getOrCreateBeanDescriptor(Model.class); Model model = new Model(); @Test public void generic() { Method method = ReflectUtils.findMethod(GenericSchema.class, "genericMethod"); BeanDescriptor beanDescriptor = beanDescriptorManager .getOrCreateBeanDescriptor(method.getGenericParameterTypes()[0]); Assertions.assertEquals(String.class, beanDescriptor.getPropertyDescriptors().get("value").getJavaType().getRawClass()); } @Test public void getOrCreate() { Assertions.assertSame(beanDescriptor, beanDescriptorManager.getOrCreateBeanDescriptor(Model.class)); Assertions.assertSame(Model.class, beanDescriptor.getJavaType().getRawClass()); } @SuppressWarnings("unchecked") @Test public void both() { PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("both"); ((IntSetter) propertyDescriptor.getSetter()).set(model, 1); Assertions.assertEquals(1, ((IntGetter) propertyDescriptor.getGetter()).get(model)); Assertions.assertEquals(1, model.getBoth()); } @SuppressWarnings("unchecked") @Test public void onlyGet() { PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("onlyGet"); Assertions.assertNull(propertyDescriptor.getSetter()); model.onlyGet(1); Assertions.assertEquals(1, ((IntGetter) propertyDescriptor.getGetter()).get(model)); Assertions.assertEquals(1, model.getOnlyGet()); } @SuppressWarnings("unchecked") @Test public void onlySet() { PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("onlySet"); Assertions.assertNull(propertyDescriptor.getGetter()); ((IntSetter) propertyDescriptor.getSetter()).set(model, 1); Assertions.assertEquals(1, model.onlySet()); } @SuppressWarnings("unchecked") @Test public void direct() { PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("direct"); ((Setter) propertyDescriptor.getSetter()).set(model, 1); Assertions.assertEquals(1, (int) ((Getter) propertyDescriptor.getGetter()).get(model)); Assertions.assertEquals(1, model.direct); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/CustomGeneric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.model; public class CustomGeneric { public T user; } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveArrays.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License")Packed; you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.model; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; public class PrimitiveArrays { private int[] int32sPacked; private long[] int64sPacked; private int[] uint32sPacked; private long[] uint64sPacked; private int[] sint32sPacked; private long[] sint64sPacked; private int[] fixed32sPacked; private long[] fixed64sPacked; private int[] sfixed32sPacked; private long[] sfixed64sPacked; private float[] floatsPacked; private double[] doublesPacked; private boolean[] boolsPacked; private Color[] colorsPacked; private int[] int32sNotPacked; private long[] int64sNotPacked; private int[] uint32sNotPacked; private long[] uint64sNotPacked; private int[] sint32sNotPacked; private long[] sint64sNotPacked; private int[] fixed32sNotPacked; private long[] fixed64sNotPacked; private int[] sfixed32sNotPacked; private long[] sfixed64sNotPacked; private float[] floatsNotPacked; private double[] doublesNotPacked; private boolean[] boolsNotPacked; private Color[] colorsNotPacked; public int[] getInt32sPacked() { return int32sPacked; } public void setInt32sPacked(int[] int32sPacked) { this.int32sPacked = int32sPacked; } public long[] getInt64sPacked() { return int64sPacked; } public void setInt64sPacked(long[] int64sPacked) { this.int64sPacked = int64sPacked; } public int[] getUint32sPacked() { return uint32sPacked; } public void setUint32sPacked(int[] uint32sPacked) { this.uint32sPacked = uint32sPacked; } public long[] getUint64sPacked() { return uint64sPacked; } public void setUint64sPacked(long[] uint64sPacked) { this.uint64sPacked = uint64sPacked; } public int[] getSint32sPacked() { return sint32sPacked; } public void setSint32sPacked(int[] sint32sPacked) { this.sint32sPacked = sint32sPacked; } public long[] getSint64sPacked() { return sint64sPacked; } public void setSint64sPacked(long[] sint64sPacked) { this.sint64sPacked = sint64sPacked; } public int[] getFixed32sPacked() { return fixed32sPacked; } public void setFixed32sPacked(int[] fixed32sPacked) { this.fixed32sPacked = fixed32sPacked; } public long[] getFixed64sPacked() { return fixed64sPacked; } public void setFixed64sPacked(long[] fixed64sPacked) { this.fixed64sPacked = fixed64sPacked; } public int[] getSfixed32sPacked() { return sfixed32sPacked; } public void setSfixed32sPacked(int[] sfixed32sPacked) { this.sfixed32sPacked = sfixed32sPacked; } public long[] getSfixed64sPacked() { return sfixed64sPacked; } public void setSfixed64sPacked(long[] sfixed64sPacked) { this.sfixed64sPacked = sfixed64sPacked; } public float[] getFloatsPacked() { return floatsPacked; } public void setFloatsPacked(float[] floatsPacked) { this.floatsPacked = floatsPacked; } public double[] getDoublesPacked() { return doublesPacked; } public void setDoublesPacked(double[] doublesPacked) { this.doublesPacked = doublesPacked; } public boolean[] getBoolsPacked() { return boolsPacked; } public void setBoolsPacked(boolean[] boolsPacked) { this.boolsPacked = boolsPacked; } public int[] getInt32sNotPacked() { return int32sNotPacked; } public void setInt32sNotPacked(int[] int32sNotPacked) { this.int32sNotPacked = int32sNotPacked; } public long[] getInt64sNotPacked() { return int64sNotPacked; } public void setInt64sNotPacked(long[] int64sNotPacked) { this.int64sNotPacked = int64sNotPacked; } public int[] getUint32sNotPacked() { return uint32sNotPacked; } public void setUint32sNotPacked(int[] uint32sNotPacked) { this.uint32sNotPacked = uint32sNotPacked; } public long[] getUint64sNotPacked() { return uint64sNotPacked; } public void setUint64sNotPacked(long[] uint64sNotPacked) { this.uint64sNotPacked = uint64sNotPacked; } public int[] getSint32sNotPacked() { return sint32sNotPacked; } public void setSint32sNotPacked(int[] sint32sNotPacked) { this.sint32sNotPacked = sint32sNotPacked; } public long[] getSint64sNotPacked() { return sint64sNotPacked; } public void setSint64sNotPacked(long[] sint64sNotPacked) { this.sint64sNotPacked = sint64sNotPacked; } public int[] getFixed32sNotPacked() { return fixed32sNotPacked; } public void setFixed32sNotPacked(int[] fixed32sNotPacked) { this.fixed32sNotPacked = fixed32sNotPacked; } public long[] getFixed64sNotPacked() { return fixed64sNotPacked; } public void setFixed64sNotPacked(long[] fixed64sNotPacked) { this.fixed64sNotPacked = fixed64sNotPacked; } public int[] getSfixed32sNotPacked() { return sfixed32sNotPacked; } public void setSfixed32sNotPacked(int[] sfixed32sNotPacked) { this.sfixed32sNotPacked = sfixed32sNotPacked; } public long[] getSfixed64sNotPacked() { return sfixed64sNotPacked; } public void setSfixed64sNotPacked(long[] sfixed64sNotPacked) { this.sfixed64sNotPacked = sfixed64sNotPacked; } public float[] getFloatsNotPacked() { return floatsNotPacked; } public void setFloatsNotPacked(float[] floatsNotPacked) { this.floatsNotPacked = floatsNotPacked; } public double[] getDoublesNotPacked() { return doublesNotPacked; } public void setDoublesNotPacked(double[] doublesNotPacked) { this.doublesNotPacked = doublesNotPacked; } public boolean[] getBoolsNotPacked() { return boolsNotPacked; } public void setBoolsNotPacked(boolean[] boolsNotPacked) { this.boolsNotPacked = boolsNotPacked; } public Color[] getColorsPacked() { return colorsPacked; } public void setColorsPacked(Color[] colorsPacked) { this.colorsPacked = colorsPacked; } public Color[] getColorsNotPacked() { return colorsNotPacked; } public void setColorsNotPacked(Color[] colorsNotPacked) { this.colorsNotPacked = colorsNotPacked; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveWrapperArrays.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License")Packed; you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.model; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; public class PrimitiveWrapperArrays { private Integer[] int32sPacked; private Long[] int64sPacked; private Integer[] uint32sPacked; private Long[] uint64sPacked; private Integer[] sint32sPacked; private Long[] sint64sPacked; private Integer[] fixed32sPacked; private Long[] fixed64sPacked; private Integer[] sfixed32sPacked; private Long[] sfixed64sPacked; private Float[] floatsPacked; private Double[] doublesPacked; private Boolean[] boolsPacked; private Color[] colorsPacked; private Integer[] int32sNotPacked; private Long[] int64sNotPacked; private Integer[] uint32sNotPacked; private Long[] uint64sNotPacked; private Integer[] sint32sNotPacked; private Long[] sint64sNotPacked; private Integer[] fixed32sNotPacked; private Long[] fixed64sNotPacked; private Integer[] sfixed32sNotPacked; private Long[] sfixed64sNotPacked; private Float[] floatsNotPacked; private Double[] doublesNotPacked; private Boolean[] boolsNotPacked; private Color[] colorsNotPacked; public Integer[] getInt32sPacked() { return int32sPacked; } public void setInt32sPacked(Integer[] int32sPacked) { this.int32sPacked = int32sPacked; } public Long[] getInt64sPacked() { return int64sPacked; } public void setInt64sPacked(Long[] int64sPacked) { this.int64sPacked = int64sPacked; } public Integer[] getUint32sPacked() { return uint32sPacked; } public void setUint32sPacked(Integer[] uint32sPacked) { this.uint32sPacked = uint32sPacked; } public Long[] getUint64sPacked() { return uint64sPacked; } public void setUint64sPacked(Long[] uint64sPacked) { this.uint64sPacked = uint64sPacked; } public Integer[] getSint32sPacked() { return sint32sPacked; } public void setSint32sPacked(Integer[] sint32sPacked) { this.sint32sPacked = sint32sPacked; } public Long[] getSint64sPacked() { return sint64sPacked; } public void setSint64sPacked(Long[] sint64sPacked) { this.sint64sPacked = sint64sPacked; } public Integer[] getFixed32sPacked() { return fixed32sPacked; } public void setFixed32sPacked(Integer[] fixed32sPacked) { this.fixed32sPacked = fixed32sPacked; } public Long[] getFixed64sPacked() { return fixed64sPacked; } public void setFixed64sPacked(Long[] fixed64sPacked) { this.fixed64sPacked = fixed64sPacked; } public Integer[] getSfixed32sPacked() { return sfixed32sPacked; } public void setSfixed32sPacked(Integer[] sfixed32sPacked) { this.sfixed32sPacked = sfixed32sPacked; } public Long[] getSfixed64sPacked() { return sfixed64sPacked; } public void setSfixed64sPacked(Long[] sfixed64sPacked) { this.sfixed64sPacked = sfixed64sPacked; } public Float[] getFloatsPacked() { return floatsPacked; } public void setFloatsPacked(Float[] floatsPacked) { this.floatsPacked = floatsPacked; } public Double[] getDoublesPacked() { return doublesPacked; } public void setDoublesPacked(Double[] doublesPacked) { this.doublesPacked = doublesPacked; } public Boolean[] getBoolsPacked() { return boolsPacked; } public void setBoolsPacked(Boolean[] boolsPacked) { this.boolsPacked = boolsPacked; } public Integer[] getInt32sNotPacked() { return int32sNotPacked; } public void setInt32sNotPacked(Integer[] int32sNotPacked) { this.int32sNotPacked = int32sNotPacked; } public Long[] getInt64sNotPacked() { return int64sNotPacked; } public void setInt64sNotPacked(Long[] int64sNotPacked) { this.int64sNotPacked = int64sNotPacked; } public Integer[] getUint32sNotPacked() { return uint32sNotPacked; } public void setUint32sNotPacked(Integer[] uint32sNotPacked) { this.uint32sNotPacked = uint32sNotPacked; } public Long[] getUint64sNotPacked() { return uint64sNotPacked; } public void setUint64sNotPacked(Long[] uint64sNotPacked) { this.uint64sNotPacked = uint64sNotPacked; } public Integer[] getSint32sNotPacked() { return sint32sNotPacked; } public void setSint32sNotPacked(Integer[] sint32sNotPacked) { this.sint32sNotPacked = sint32sNotPacked; } public Long[] getSint64sNotPacked() { return sint64sNotPacked; } public void setSint64sNotPacked(Long[] sint64sNotPacked) { this.sint64sNotPacked = sint64sNotPacked; } public Integer[] getFixed32sNotPacked() { return fixed32sNotPacked; } public void setFixed32sNotPacked(Integer[] fixed32sNotPacked) { this.fixed32sNotPacked = fixed32sNotPacked; } public Long[] getFixed64sNotPacked() { return fixed64sNotPacked; } public void setFixed64sNotPacked(Long[] fixed64sNotPacked) { this.fixed64sNotPacked = fixed64sNotPacked; } public Integer[] getSfixed32sNotPacked() { return sfixed32sNotPacked; } public void setSfixed32sNotPacked(Integer[] sfixed32sNotPacked) { this.sfixed32sNotPacked = sfixed32sNotPacked; } public Long[] getSfixed64sNotPacked() { return sfixed64sNotPacked; } public void setSfixed64sNotPacked(Long[] sfixed64sNotPacked) { this.sfixed64sNotPacked = sfixed64sNotPacked; } public Float[] getFloatsNotPacked() { return floatsNotPacked; } public void setFloatsNotPacked(Float[] floatsNotPacked) { this.floatsNotPacked = floatsNotPacked; } public Double[] getDoublesNotPacked() { return doublesNotPacked; } public void setDoublesNotPacked(Double[] doublesNotPacked) { this.doublesNotPacked = doublesNotPacked; } public Boolean[] getBoolsNotPacked() { return boolsNotPacked; } public void setBoolsNotPacked(Boolean[] boolsNotPacked) { this.boolsNotPacked = boolsNotPacked; } public Color[] getColorsPacked() { return colorsPacked; } public void setColorsPacked(Color[] colorsPacked) { this.colorsPacked = colorsPacked; } public Color[] getColorsNotPacked() { return colorsNotPacked; } public void setColorsNotPacked(Color[] colorsNotPacked) { this.colorsNotPacked = colorsNotPacked; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/Root.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.model; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import io.protostuff.Tag; /** * Tag annotation is only for protoStuff, not necessary for ServiceComb codec */ public class Root { @Tag(1) private int int32; @Tag(2) private long int64; @Tag(3) private int uint32; @Tag(4) private long uint64; @Tag(5) private int sint32; @Tag(6) private long sint64; @Tag(7) private int fixed32; @Tag(8) private long fixed64; @Tag(9) private int sfixed32; @Tag(10) private long sfixed64; @Tag(11) private float floatValue; @Tag(12) private double doubleValue; @Tag(13) private boolean bool; @Tag(20) private Integer objInt32; @Tag(21) private Long objInt64; @Tag(22) private Integer objUint32; @Tag(23) private Long objUint64; @Tag(24) private Integer objSint32; @Tag(25) private Long objSint64; @Tag(26) private Integer objFixed32; @Tag(27) private Long objFixed64; @Tag(28) private Integer objSfixed32; @Tag(29) private Long objSfixed64; @Tag(30) private Float objFloatValue; @Tag(31) private Double objDoubleValue; @Tag(32) private Boolean objBool; @Tag(40) private String string; @Tag(41) private byte[] bytes; @Tag(42) private Color color; @Tag(43) private User user; @Tag(44) private Root typeRecursive; @Tag(50) private Object any; @Tag(51) private List anys; @Tag(60) private Map ssMap; @Tag(61) private Map sint32Map; @Tag(62) private Map spMap; // repeated packed @Tag(70) private List int32sPacked; @Tag(71) private List int64sPacked; @Tag(72) private List uint32sPacked; @Tag(73) private List uint64sPacked; @Tag(74) private List sint32sPacked; @Tag(75) private List sint64sPacked; @Tag(76) private List fixed32sPacked; @Tag(77) private List fixed64sPacked; @Tag(78) private List sfixed32sPacked; @Tag(79) private List sfixed64sPacked; @Tag(80) private List floatsPacked; @Tag(81) private List doublesPacked; @Tag(82) private List boolsPacked; @Tag(83) private List colorsPacked; // repeated not packed @Tag(90) private List int32sNotPacked; @Tag(91) private List int64sNotPacked; @Tag(92) private List uint32sNotPacked; @Tag(93) private List uint64sNotPacked; @Tag(94) private List sint32sNotPacked; @Tag(95) private List sint64sNotPacked; @Tag(96) private List fixed32sNotPacked; @Tag(97) private List fixed64sNotPacked; @Tag(98) private List sfixed32sNotPacked; @Tag(99) private List sfixed64sNotPacked; @Tag(100) private List floatsNotPacked; @Tag(101) private List doublesNotPacked; @Tag(102) private List boolsNotPacked; @Tag(103) private List colorsNotPacked; @Tag(110) private List strings; @Tag(111) private List bytess; @Tag(112) private List users; public int getInt32() { return int32; } public void setInt32(int int32) { this.int32 = int32; } public long getInt64() { return int64; } public void setInt64(long int64) { this.int64 = int64; } public int getUint32() { return uint32; } public void setUint32(int uint32) { this.uint32 = uint32; } public long getUint64() { return uint64; } public void setUint64(long uint64) { this.uint64 = uint64; } public int getSint32() { return sint32; } public void setSint32(int sint32) { this.sint32 = sint32; } public long getSint64() { return sint64; } public void setSint64(long sint64) { this.sint64 = sint64; } public int getFixed32() { return fixed32; } public void setFixed32(int fixed32) { this.fixed32 = fixed32; } public long getFixed64() { return fixed64; } public void setFixed64(long fixed64) { this.fixed64 = fixed64; } public int getSfixed32() { return sfixed32; } public void setSfixed32(int sfixed32) { this.sfixed32 = sfixed32; } public long getSfixed64() { return sfixed64; } public void setSfixed64(long sfixed64) { this.sfixed64 = sfixed64; } public float getFloatValue() { return floatValue; } public void setFloatValue(float floatValue) { this.floatValue = floatValue; } public double getDoubleValue() { return doubleValue; } public void setDoubleValue(double doubleValue) { this.doubleValue = doubleValue; } public boolean isBool() { return bool; } public void setBool(boolean bool) { this.bool = bool; } public String getString() { return string; } public void setString(String string) { this.string = string; } public byte[] getBytes() { return bytes; } public void setBytes(byte[] bytes) { this.bytes = bytes; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Map getSsMap() { return ssMap; } public void setSsMap(Map ssMap) { this.ssMap = ssMap; } public Map getSpMap() { return spMap; } public void setSpMap(Map spMap) { this.spMap = spMap; } public Object getAny() { return any; } public void setAny(Object any) { this.any = any; } public List getAnys() { return anys; } public void setAnys(List anys) { this.anys = anys; } public Root getTypeRecursive() { return typeRecursive; } public void setTypeRecursive(Root typeRecursive) { this.typeRecursive = typeRecursive; } public List getInt32sPacked() { return int32sPacked; } public void setInt32sPacked(List int32sPacked) { this.int32sPacked = int32sPacked; } public List getInt64sPacked() { return int64sPacked; } public void setInt64sPacked(List int64sPacked) { this.int64sPacked = int64sPacked; } public List getUint32sPacked() { return uint32sPacked; } public void setUint32sPacked(List uint32sPacked) { this.uint32sPacked = uint32sPacked; } public List getUint64sPacked() { return uint64sPacked; } public void setUint64sPacked(List uint64sPacked) { this.uint64sPacked = uint64sPacked; } public List getSint32sPacked() { return sint32sPacked; } public void setSint32sPacked(List sint32sPacked) { this.sint32sPacked = sint32sPacked; } public List getSint64sPacked() { return sint64sPacked; } public void setSint64sPacked(List sint64sPacked) { this.sint64sPacked = sint64sPacked; } public List getFixed32sPacked() { return fixed32sPacked; } public void setFixed32sPacked(List fixed32sPacked) { this.fixed32sPacked = fixed32sPacked; } public List getFixed64sPacked() { return fixed64sPacked; } public void setFixed64sPacked(List fixed64sPacked) { this.fixed64sPacked = fixed64sPacked; } public List getSfixed32sPacked() { return sfixed32sPacked; } public void setSfixed32sPacked(List sfixed32sPacked) { this.sfixed32sPacked = sfixed32sPacked; } public List getSfixed64sPacked() { return sfixed64sPacked; } public void setSfixed64sPacked(List sfixed64sPacked) { this.sfixed64sPacked = sfixed64sPacked; } public List getFloatsPacked() { return floatsPacked; } public void setFloatsPacked(List floatsPacked) { this.floatsPacked = floatsPacked; } public List getDoublesPacked() { return doublesPacked; } public void setDoublesPacked(List doublesPacked) { this.doublesPacked = doublesPacked; } public List getBoolsPacked() { return boolsPacked; } public void setBoolsPacked(List boolsPacked) { this.boolsPacked = boolsPacked; } public List getColorsPacked() { return colorsPacked; } public void setColorsPacked(List colorsPacked) { this.colorsPacked = colorsPacked; } public List getInt32sNotPacked() { return int32sNotPacked; } public void setInt32sNotPacked(List int32sNotPacked) { this.int32sNotPacked = int32sNotPacked; } public List getInt64sNotPacked() { return int64sNotPacked; } public void setInt64sNotPacked(List int64sNotPacked) { this.int64sNotPacked = int64sNotPacked; } public List getUint32sNotPacked() { return uint32sNotPacked; } public void setUint32sNotPacked(List uint32sNotPacked) { this.uint32sNotPacked = uint32sNotPacked; } public List getUint64sNotPacked() { return uint64sNotPacked; } public void setUint64sNotPacked(List uint64sNotPacked) { this.uint64sNotPacked = uint64sNotPacked; } public List getSint32sNotPacked() { return sint32sNotPacked; } public void setSint32sNotPacked(List sint32sNotPacked) { this.sint32sNotPacked = sint32sNotPacked; } public List getSint64sNotPacked() { return sint64sNotPacked; } public void setSint64sNotPacked(List sint64sNotPacked) { this.sint64sNotPacked = sint64sNotPacked; } public List getFixed32sNotPacked() { return fixed32sNotPacked; } public void setFixed32sNotPacked(List fixed32sNotPacked) { this.fixed32sNotPacked = fixed32sNotPacked; } public List getFixed64sNotPacked() { return fixed64sNotPacked; } public void setFixed64sNotPacked(List fixed64sNotPacked) { this.fixed64sNotPacked = fixed64sNotPacked; } public List getSfixed32sNotPacked() { return sfixed32sNotPacked; } public void setSfixed32sNotPacked(List sfixed32sNotPacked) { this.sfixed32sNotPacked = sfixed32sNotPacked; } public List getSfixed64sNotPacked() { return sfixed64sNotPacked; } public void setSfixed64sNotPacked(List sfixed64sNotPacked) { this.sfixed64sNotPacked = sfixed64sNotPacked; } public List getFloatsNotPacked() { return floatsNotPacked; } public void setFloatsNotPacked(List floatsNotPacked) { this.floatsNotPacked = floatsNotPacked; } public List getDoublesNotPacked() { return doublesNotPacked; } public void setDoublesNotPacked(List doublesNotPacked) { this.doublesNotPacked = doublesNotPacked; } public List getBoolsNotPacked() { return boolsNotPacked; } public void setBoolsNotPacked(List boolsNotPacked) { this.boolsNotPacked = boolsNotPacked; } public List getColorsNotPacked() { return colorsNotPacked; } public void setColorsNotPacked( List colorsNotPacked) { this.colorsNotPacked = colorsNotPacked; } public List getStrings() { return strings; } public void setStrings(List strings) { this.strings = strings; } public List getBytess() { return bytess; } public void setBytess(List bytess) { this.bytess = bytess; } public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } public Integer getObjInt32() { return objInt32; } public void setObjInt32(Integer objInt32) { this.objInt32 = objInt32; } public Long getObjInt64() { return objInt64; } public void setObjInt64(Long objInt64) { this.objInt64 = objInt64; } public Integer getObjUint32() { return objUint32; } public void setObjUint32(Integer objUint32) { this.objUint32 = objUint32; } public Long getObjUint64() { return objUint64; } public void setObjUint64(Long objUint64) { this.objUint64 = objUint64; } public Integer getObjSint32() { return objSint32; } public void setObjSint32(Integer objSint32) { this.objSint32 = objSint32; } public Long getObjSint64() { return objSint64; } public void setObjSint64(Long objSint64) { this.objSint64 = objSint64; } public Integer getObjFixed32() { return objFixed32; } public void setObjFixed32(Integer objFixed32) { this.objFixed32 = objFixed32; } public Long getObjFixed64() { return objFixed64; } public void setObjFixed64(Long objFixed64) { this.objFixed64 = objFixed64; } public Integer getObjSfixed32() { return objSfixed32; } public void setObjSfixed32(Integer objSfixed32) { this.objSfixed32 = objSfixed32; } public Long getObjSfixed64() { return objSfixed64; } public void setObjSfixed64(Long objSfixed64) { this.objSfixed64 = objSfixed64; } public Float getObjFloatValue() { return objFloatValue; } public void setObjFloatValue(Float objFloatValue) { this.objFloatValue = objFloatValue; } public Double getObjDoubleValue() { return objDoubleValue; } public void setObjDoubleValue(Double objDoubleValue) { this.objDoubleValue = objDoubleValue; } public Boolean isObjBool() { return objBool; } public void setObjBool(Boolean objBool) { this.objBool = objBool; } public Map getSint32Map() { return sint32Map; } public void setSint32Map(Map sint32Map) { this.sint32Map = sint32Map; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.model; public class User { private String name; private Root typeRecursive; public User() { } public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User name(String name) { this.name = name; return this; } public Root getTypeRecursive() { return typeRecursive; } public void setTypeRecursive(Root typeRecursive) { this.typeRecursive = typeRecursive; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/parser/TestProtoParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.parser; import com.google.common.base.MoreObjects; import io.protostuff.compiler.model.Proto; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; public class TestProtoParser { @Test public void parse() throws IOException { URL url = Thread.currentThread().getContextClassLoader().getResource("protobufRoot.proto"); String content = IOUtils.toString(url, StandardCharsets.UTF_8); ProtoParser parser = new ProtoParser(); Proto protoFromContent = parser.parseFromContent(content); Proto protoFromName = parser.parse("protobufRoot.proto"); Assertions.assertNotNull(protoFromContent.getMessage("Root")); Assertions.assertNotNull(protoFromContent.getMessage("User")); Assertions.assertEquals(MoreObjects.toStringHelper(protoFromContent) .omitNullValues() .add("messages", protoFromContent.getMessages()) .toString(), MoreObjects.toStringHelper(protoFromName) .omitNullValues() .add("messages", protoFromName.getMessages()) .toString()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestAnySchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import com.google.protobuf.Any; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAnySchema extends TestSchemaBase { @Test public void empty() throws Throwable { scbMap = new HashMap<>(); scbMap.put("any", null); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); } @Test public void anys_pack() throws IOException { builder .addAnys(Any.pack(ProtobufRoot.User.newBuilder().setName("n1").build())) .addAnys(Any.pack(ProtobufRoot.User.newBuilder().setName("n2").build())); check(); } @Test public void anys_json() throws IOException { Root root = new Root(); root.setAnys(Arrays.asList("abc", "123")); scbRootBytes = rootSerializer.serialize(root); root = rootDeserializer.deserialize(scbRootBytes); MatcherAssert.assertThat(root.getAnys(), Matchers.contains("abc", "123")); } @Test public void pack() throws Throwable { builder.setAny(Any.pack(ProtobufRoot.User.newBuilder().setName("n1").build())); check(); Map map = new HashMap<>(); map.put(ProtoConst.JSON_ID_NAME, "User"); map.put("name", "n1"); Root root = new Root(); root.setAny(map); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(root)); } @SuppressWarnings("unchecked") @Test public void json_fromMapWithoutType() throws Throwable { Map map = new HashMap<>(); map.put("name", "n1"); Root root = new Root(); root.setAny(map); scbRootBytes = rootSerializer.serialize(root); root = rootDeserializer.deserialize(scbRootBytes); MatcherAssert.assertThat(root.getAny(), Matchers.instanceOf(Map.class)); MatcherAssert.assertThat((Map) root.getAny(), Matchers.hasEntry("name", "n1")); RootDeserializer> deserializer = protoMapper.createRootDeserializer("Root", Map.class); map = deserializer.deserialize(scbRootBytes); MatcherAssert.assertThat((Map) map.get("any"), Matchers.hasEntry("name", "n1")); } @Test public void json() throws Throwable { Root root = new Root(); root.setAny("abc"); scbRootBytes = rootSerializer.serialize(root); root = rootDeserializer.deserialize(scbRootBytes); Assertions.assertEquals("abc", root.getAny()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMapSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.junit.jupiter.api.Test; public class TestMapSchema extends TestSchemaBase { @Test public void ssMap() throws Throwable { Map ssMap = new HashMap<>(); ssMap.put("k1", "v1"); ssMap.put("k2", "v2"); builder.putAllSsMap(ssMap); check(); } @Test public void spMap() throws Throwable { builder.putSpMap("k1", ProtobufRoot.User.newBuilder().setName("n1").build()); builder.putSpMap("k2", ProtobufRoot.User.newBuilder().setName("n2").build()); check(); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMessageSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.util.LinkedHashMap; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.CustomGeneric; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMessageSchema extends TestSchemaBase { @Test public void empty() throws Throwable { check(); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(null)); } @Test public void generic() throws Throwable { JavaType javaType = TypeFactory.defaultInstance().constructParametricType(CustomGeneric.class, User.class); RootDeserializer> genericDeserializer = protoMapper.createRootDeserializer("Root", javaType); builder.setUser(ProtobufRoot.User.newBuilder().setName("name1").build()); check(genericDeserializer, mapRootDeserializer, rootSerializer, false); @SuppressWarnings("unchecked") CustomGeneric generic = (CustomGeneric) scbRoot; Assertions.assertNotNull(generic.user); } @Test public void normal() throws Throwable { builder.setString("abc"); builder.setInt64(1L); builder.setUser(ProtobufRoot.User.newBuilder().setName("name").build()); check(); // map Map map = new LinkedHashMap<>(); map.put("int64", 1); map.put("string", "abc"); Map userMap = new LinkedHashMap<>(); userMap.put("name", "name"); map.put("user", userMap); map.put("notExist", null); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestRepeatedSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.Color; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import com.google.protobuf.ByteString; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRepeatedSchema extends TestSchemaBase { public static class RootWithArray { public byte[][] bytess; public String[] strings; public User[] users; } @Test public void fixed32s() throws Exception { builder.addAllFixed32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void fixed32sNotPacked() throws Exception { builder.addAllFixed32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sfixed32s() throws Exception { builder.addAllSfixed32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sfixed32sNotPacked() throws Exception { builder.addAllSfixed32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void int32s() throws Exception { builder.addAllInt32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void int32sNotPacked() throws Exception { builder.addAllInt32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sint32s() throws Exception { builder.addAllSint32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sint32sNotPacked() throws Exception { builder.addAllSint32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void uint32s() throws Exception { builder.addAllUint32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void uint32sNotPacked() throws Exception { builder.addAllUint32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void fixed64s() throws Exception { builder.addAllFixed64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void fixed64sNotPacked() throws Exception { builder.addAllFixed64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sfixed64s() throws Exception { builder.addAllSfixed64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sfixed64sNotPacked() throws Exception { builder.addAllSfixed64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void int64s() throws Exception { builder.addAllInt64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void int64sNotPacked() throws Exception { builder.addAllInt64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sint64s() throws Exception { builder.addAllSint64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void sint64sNotPacked() throws Exception { builder.addAllSint64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void uint64s() throws Exception { builder.addAllUint64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void uint64sNotPacked() throws Exception { builder.addAllUint64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void floats() throws Exception { builder.addAllFloatsPacked(Arrays.asList(Float.MIN_VALUE, (float) 0.0, Float.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void floatsNotPacked() throws Exception { builder.addAllFloatsNotPacked(Arrays.asList(Float.MIN_VALUE, (float) 0.0, Float.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void doubles() throws Exception { builder.addAllDoublesPacked(Arrays.asList(Double.MIN_VALUE, 0.0, Double.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void doublesNotPacked() throws Exception { builder.addAllDoublesNotPacked(Arrays.asList(Double.MIN_VALUE, 0.0, Double.MAX_VALUE)); checkRepeatedWithPrimitive(); } @Test public void bools() throws Exception { builder.addAllBoolsPacked(Arrays.asList(Boolean.FALSE, Boolean.TRUE)); checkRepeatedWithPrimitive(); } @Test public void boolsNotPacked() throws Exception { builder.addAllBoolsNotPacked(Arrays.asList(Boolean.FALSE, Boolean.TRUE)); checkRepeatedWithPrimitive(); } @Test public void enums() throws Exception { builder.addAllColorsPacked(Arrays.asList(Color.RED, Color.BLUE)); checkRepeatedWithPrimitive(); } @Test public void enumsNotPacked() throws Exception { builder.addAllColorsNotPacked(Arrays.asList(Color.RED, Color.BLUE)); checkRepeatedWithPrimitive(); } @Test public void bytess() throws Throwable { List sList = Arrays.asList("v1".getBytes(StandardCharsets.UTF_8), "v2".getBytes(StandardCharsets.UTF_8)); builder.addAllBytess(Arrays.asList(ByteString.copyFromUtf8("v1"), ByteString.copyFromUtf8("v2"))); check(); RootWithArray rootWithArray = new RootWithArray(); rootWithArray.bytess = sList.toArray(new byte[0][]); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(rootWithArray)); } @Test public void strings() throws Throwable { List sList = Arrays.asList("v1", "v2"); builder.addAllStrings(sList); check(); RootWithArray rootWithArray = new RootWithArray(); rootWithArray.strings = sList.toArray(new String[0]); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(rootWithArray)); } @Test public void users() throws Throwable { builder.addUsers(ProtobufRoot.User.newBuilder().setName("name1").build()); builder.addUsers(ProtobufRoot.User.newBuilder().setName("name2").build()); check(); RootWithArray rootWithArray = new RootWithArray(); rootWithArray.users = new User[] {new User("name1"), new User("name2")}; Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(rootWithArray)); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; import java.util.HashMap; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBoolSchema extends TestSchemaBase { public TestBoolSchema() { initFields("bool", "objBool"); } @Test public void testTrue() throws Throwable { // normal builder.setBool(true); check(); // equalsIgnoreCase doTestFromString("true"); doTestFromString("true"); } @Test public void testFalse() throws Throwable { // normal builder.setBool(false); check(); // all not true is false doTestFromString("false"); doTestFromString("abcd"); } protected void doTestFromString(String value) throws Throwable { // string[] scbMap = new HashMap<>(); scbMap.put("bool", new String[] {value}); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); // string scbMap.put("bool", value); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); } @Test public void nullOrEmpty() throws Throwable { // null scbMap = new HashMap<>(); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); // empty string[] scbMap.put("bool", new String[] {}); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); } @Test public void type_invalid() throws Throwable { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put("bool", new User()); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto bool, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:bool", exception.getMessage()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBytesSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; import java.util.HashMap; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import com.google.protobuf.ByteString; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBytesSchema extends TestSchemaBase { public TestBytesSchema() { initField("bytes"); } @Test public void normal() throws Throwable { byte[] value = "abc".getBytes(); builder.setBytes(ByteString.copyFrom(value)); check(); } @Test public void type_invalid() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put("bytes", new User()); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto bytes, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:bytes", exception.getMessage()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestDoubleSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestDoubleSchema extends TestNumberBaseSchema { public TestDoubleSchema() { minValue = Double.MIN_VALUE; maxValue = Double.MAX_VALUE; initFields("doubleValue", "objDoubleValue"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestEnumSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestEnumSchema extends TestSchemaBase { @Test public void empty() throws Throwable { // null scbMap = new HashMap<>(); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); // empty string[] scbMap.put("color", new String[] {}); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); } public static class EnumRoot { private int color; public int getColor() { return color; } public void setColor(int color) { this.color = color; } } @Test public void normal() throws Throwable { builder.setColorValue(2); check(); Map map = new HashMap<>(); map.put("color", Color.BLUE); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); map.put("color", 2); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); map.put("color", new String[] {"BLUE"}); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); map.put("color", "BLUE"); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); EnumRoot enumRoot = protoMapper.createRootDeserializer("Root", EnumRoot.class).deserialize(protobufBytes); Assertions.assertEquals(2, enumRoot.color); } enum Sharp { ROUND } @Test public void fromInvalidEnum() throws Throwable { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put("color", Sharp.ROUND); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("invalid enum name ROUND for proto Color, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:color", exception.getMessage()); } @Test public void fromInvalidNumber() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put("color", 3); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("invalid enum value 3 for proto Color, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:color", exception.getMessage()); } @Test public void type_invalid() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put("color", new User()); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto Color, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:color", exception.getMessage()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed32Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestFixed32Schema extends TestNumberBaseSchema { public TestFixed32Schema() { minValue = Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; initFields("fixed32", "objFixed32"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed64Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestFixed64Schema extends TestNumberBaseSchema { public TestFixed64Schema() { minValue = Long.MIN_VALUE; maxValue = Long.MAX_VALUE; initFields("fixed64", "objFixed64"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFloatSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestFloatSchema extends TestNumberBaseSchema { public TestFloatSchema() { minValue = Float.MIN_VALUE; maxValue = Float.MAX_VALUE; initFields("floatValue", "objFloatValue"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt32Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestInt32Schema extends TestNumberBaseSchema { public TestInt32Schema() { minValue = Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; initFields("int32", "objInt32"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt64Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestInt64Schema extends TestNumberBaseSchema { public TestInt64Schema() { minValue = Long.MIN_VALUE; maxValue = Long.MAX_VALUE; initFields("int64", "objInt64"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestNumberBaseSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; import java.io.IOException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Locale; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import io.protostuff.compiler.model.Field; import io.protostuff.compiler.model.Type; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @Disabled public abstract class TestNumberBaseSchema extends TestSchemaBase { protected Object minValue; protected Object maxValue; @Test public void normal() throws Throwable { byte[] primitiveBytes = doTestPojoNormal(primitiveFieldName); byte[] bytes = doTestPojoNormal(fieldName); doTestMapNormal(primitiveFieldName, maxValue, primitiveBytes); doTestMapNormal(fieldName, maxValue, bytes); } private void doTestMapNormal(String field, Object value, byte[] expectBytes) throws IOException { // null scbMap = new HashMap<>(); scbMap.put(field, null); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); // empty string[] scbMap.put(field, new String[] {}); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); // string[] scbMap.put(field, new String[] {String.valueOf(value)}); Assertions.assertArrayEquals(expectBytes, rootSerializer.serialize(scbMap)); // string scbMap.put(field, String.valueOf(value)); Assertions.assertArrayEquals(expectBytes, rootSerializer.serialize(scbMap)); } private byte[] doTestPojoNormal(String name) throws Throwable { builder = ProtobufRoot.Root.newBuilder(); String setName = "set" + name.substring(0, 1).toUpperCase(Locale.US) + name.substring(1); Method builderSetter = ReflectUtils.findMethod(builder.getClass(), setName); builderSetter.invoke(builder, minValue); check(); builderSetter.invoke(builder, maxValue); check(); return protobufBytes; } @Test public void primitiveStrings_invalid() throws Throwable { strings_invalid(primitiveProtoField); } @Test public void strings_invalid() throws Throwable { strings_invalid(protoField); } private void strings_invalid(Field field) { NumberFormatException exception = Assertions.assertThrows(NumberFormatException.class, () -> { scbMap = new HashMap<>(); scbMap.put(field.getName(), new String[] {"a"}); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("For input string: \"a\"", exception.getMessage()); } @Test public void primitiveString_invalid() throws Throwable { string_invalid(primitiveProtoField); } @Test public void string_invalid() throws Throwable { string_invalid(protoField); } private void string_invalid(Field field) { NumberFormatException exception = Assertions.assertThrows(NumberFormatException.class, () -> { scbMap = new HashMap<>(); scbMap.put(field.getName(), "a"); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("For input string: \"a\"", exception.getMessage()); } @Test public void primitiveType_invalid() { type_invalid(primitiveProtoField); } @Test public void type_invalid() throws Throwable { type_invalid(protoField); } private void type_invalid(Field field) { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put(field.getName(), new User()); rootSerializer.serialize(scbMap); }); Assertions.assertEquals(String.format("not support serialize from %s to proto %s, field=%s:%s", User.class.getName(), field.getTypeName(), ((Type) field.getParent()).getCanonicalName(), field.getName()), exception.getMessage()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed32Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestSFixed32Schema extends TestNumberBaseSchema { public TestSFixed32Schema() { minValue = Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; initFields("sfixed32", "objSfixed32"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed64Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestSFixed64Schema extends TestNumberBaseSchema { public TestSFixed64Schema() { minValue = Long.MIN_VALUE; maxValue = Long.MAX_VALUE; initFields("sfixed64", "objSfixed64"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt32Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestSInt32Schema extends TestNumberBaseSchema { public TestSInt32Schema() { minValue = Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; initFields("sint32", "objSint32"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt64Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestSInt64Schema extends TestNumberBaseSchema { public TestSInt64Schema() { minValue = Long.MIN_VALUE; maxValue = Long.MAX_VALUE; initFields("sint64", "objSint64"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestStringSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; import java.util.HashMap; import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestStringSchema extends TestSchemaBase { @Test public void normal() throws Throwable { String value = "abc"; builder.setString(value); check(); // string[] scbMap = new HashMap<>(); scbMap.put("string", new String[] {value}); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); // string scbMap.put("string", value); Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); } @Test public void nullOrEmpty() throws Throwable { // null scbMap = new HashMap<>(); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); // empty string[] scbMap.put("string", new String[] {}); Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); } @Test public void type_invalid() throws Throwable { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { scbMap = new HashMap<>(); scbMap.put("string", new User()); rootSerializer.serialize(scbMap); }); Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto string, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:string", exception.getMessage()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt32Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestUInt32Schema extends TestNumberBaseSchema { public TestUInt32Schema() { minValue = Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; initFields("uint32", "objUint32"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt64Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; public class TestUInt64Schema extends TestNumberBaseSchema { public TestUInt64Schema() { minValue = Long.MIN_VALUE; maxValue = Long.MAX_VALUE; initFields("uint64", "objUint64"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/ProtubufCodecEngine.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance; import java.io.IOException; public interface ProtubufCodecEngine { byte[] serialize(Object model) throws IOException; Object deserialize(byte[] bytes) throws IOException; } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance; import java.io.IOException; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; import org.apache.servicecomb.foundation.protobuf.performance.engine.Jackson; import org.apache.servicecomb.foundation.protobuf.performance.engine.Protobuf; import org.apache.servicecomb.foundation.protobuf.performance.engine.Protostuff; import org.apache.servicecomb.foundation.protobuf.performance.engine.ScbStrong; import org.apache.servicecomb.foundation.protobuf.performance.engine.ScbWeak; public abstract class TestBase { public interface Case { TestEngineResult run(); } static ProtubufCodecEngine scbStrong = new ScbStrong(); static ProtubufCodecEngine scbWeak = new ScbWeak(); static ProtubufCodecEngine protoStuff = new Protostuff(); static ProtubufCodecEngine protobuf = new Protobuf(); static ProtubufCodecEngine jackson = new Jackson(); protected Root pojoRoot = new Root(); protected Map pojoRootMap; protected ProtobufRoot.Root.Builder builder = ProtobufRoot.Root.newBuilder(); @SuppressWarnings("unchecked") public TestResult run(int count) throws IOException { pojoRootMap = (Map) Jackson.jsonMapper.convertValue(pojoRoot, Map.class); // warm doRun(10_000); // real test return doRun(count); } private TestResult doRun(int count) throws IOException { TestResult testResult = new TestResult(); testResult.name = this.getClass().getSimpleName(); testResult.addTestEngineResult(runOneEngine(protoStuff, pojoRoot, count)); testResult.addTestEngineResult(runOneEngine(scbStrong, pojoRoot, count)); testResult.addTestEngineResult(runOneEngine(scbWeak, pojoRootMap, count)); testResult.addTestEngineResult(runOneEngine(protobuf, builder, count)); testResult.addTestEngineResult(runOneEngine(jackson, pojoRoot, count)); return testResult; } private TestEngineResult runOneEngine(ProtubufCodecEngine engine, Object model, int count) throws IOException { TestEngineResult engineResult = new TestEngineResult(); engineResult.engineName = engine.getClass().getSimpleName(); // serialize { long msStart = System.currentTimeMillis(); for (int idx = 0; idx < count; idx++) { engineResult.serializeBytes = engine.serialize(model); } long msEnd = System.currentTimeMillis(); engineResult.msSerializeTime = msEnd - msStart; } // deserialize { long msStart = System.currentTimeMillis(); for (int idx = 0; idx < count; idx++) { engineResult.deserializeResult = engine.deserialize(engineResult.serializeBytes); } long msEnd = System.currentTimeMillis(); engineResult.msDeserializeTime = msEnd - msStart; } engineResult.deserializeResultBytes = engine.serialize(engineResult.deserializeResult); return engineResult; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance; public class TestEngineResult { public String engineName; // serialize public long msSerializeTime; public byte[] serializeBytes; // deserialize public long msDeserializeTime; public Object deserializeResult; public byte[] deserializeResultBytes; } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance; import java.io.IOException; import org.apache.servicecomb.foundation.protobuf.performance.cases.Empty; import org.apache.servicecomb.foundation.protobuf.performance.cases.Map; import org.apache.servicecomb.foundation.protobuf.performance.cases.Mixed; import org.apache.servicecomb.foundation.protobuf.performance.cases.Pojo; import org.apache.servicecomb.foundation.protobuf.performance.cases.PojoList; import org.apache.servicecomb.foundation.protobuf.performance.cases.Scalars; import org.apache.servicecomb.foundation.protobuf.performance.cases.SimpleList; import com.google.common.base.Strings; public class TestProtoPerformance { public static void main(String[] args) throws IOException { System.out.println("1.protobuf\n" + " in our real scenes\n" + " business model never bind to transport, and can switch between different transports dynamically\n" + " that means if we choose standard protobuf, must build protobuf models from business models each time\n" + " so should be much slower than the test results"); System.out.println("2.protoStuff\n" + " some scenes, there is no field but have getter or setter, so we can not use unsafe to access field\n" + " so we disable protoStuff unsafe feature\n\n" + " for repeated fields, protoStuff have better performance, but not compatible to protobuf\n"); System.out.println("3.jackson\n" + " not support map/any/recursive, ignore related fields"); System.out.println("4.serialize result size\n" + " ScbStrong/ScbWeak/Protobuf have the same and smaller size, because skip all default/null value"); System.setProperty("protostuff.runtime.use_sun_misc_unsafe", "false"); int count = 50_0000; printResult(new Empty().run(count)); printResult(new Scalars().run(count)); printResult(new Pojo().run(count)); printResult(new SimpleList().run(count)); printResult(new PojoList().run(count)); printResult(new Map().run(count)); printResult(new Mixed().run(count)); } private static void printResult(TestResult result) { String strFmt = Strings.repeat("%-11s", result.engineResults.size()); String numberFmt = Strings.repeat("%-11d", result.engineResults.size()); System.out.println(result.name + ": "); System.out.printf(" " + strFmt + "\n", result.engineResults.stream().map(r -> r.engineName).toArray()); System.out.printf("serialize time(ms) : " + numberFmt + "\n", result.engineResults.stream().map(r -> r.msSerializeTime).toArray()); System.out.printf("serialize len : " + numberFmt + "\n", result.engineResults.stream().map(r -> r.serializeBytes.length).toArray()); System.out.printf("deserialize time(ms): " + numberFmt + "\n", result.engineResults.stream().map(r -> r.msDeserializeTime).toArray()); System.out.printf("deserialize->serialize len: " + numberFmt + "\n", result.engineResults.stream().map(r -> r.deserializeResultBytes.length).toArray()); System.out.printf("serialize+deserialize(ms) : " + numberFmt + "\n\n", result.engineResults.stream().map(r -> r.msSerializeTime + r.msDeserializeTime).toArray()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestResult.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance; import java.util.ArrayList; import java.util.List; public class TestResult { public String name; public List engineResults = new ArrayList<>(); public void addTestEngineResult(TestEngineResult engineResult) { engineResults.add(engineResult); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Empty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; public class Empty extends TestBase { } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Map.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import java.util.HashMap; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; public class Map extends TestBase { public Map() { pojoRoot.setSsMap(new HashMap<>()); pojoRoot.getSsMap().put("k1", "v1"); pojoRoot.getSsMap().put("k2", "v2"); pojoRoot.setSpMap(new HashMap<>()); pojoRoot.getSpMap().put("u1", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1")); pojoRoot.getSpMap().put("u2", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2")); builder.putSsMap("k1", "v1") .putSsMap("k2", "v2") .putSpMap("u1", User.newBuilder().setName("name1").build()) .putSpMap("u2", User.newBuilder().setName("name2").build()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Mixed.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import java.util.Arrays; import java.util.LinkedHashMap; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; public class Mixed extends TestBase { public Mixed() { pojoRoot.setInt32(10000); pojoRoot.setInt64(20000L); pojoRoot.setUint32(30000); pojoRoot.setUint64(40000L); pojoRoot.setSint32(50000); pojoRoot.setSint64(60000L); pojoRoot.setFixed32(70000); pojoRoot.setFixed64(80000L); pojoRoot.setSfixed32(90000); pojoRoot.setSfixed64(100000L); pojoRoot.setFloatValue((float) 10000); pojoRoot.setDoubleValue(20000.0); pojoRoot.setBool(true); pojoRoot.setObjInt32(10000); pojoRoot.setObjInt64(20000L); pojoRoot.setObjUint32(30000); pojoRoot.setObjUint64(40000L); pojoRoot.setObjSint32(50000); pojoRoot.setObjSint64(60000L); pojoRoot.setObjFixed32(70000); pojoRoot.setObjFixed64(80000L); pojoRoot.setObjSfixed32(90000); pojoRoot.setObjSfixed64(100000L); pojoRoot.setObjFloatValue((float) 10000); pojoRoot.setObjDoubleValue(20000.0); pojoRoot.setObjBool(true); pojoRoot.setString("string value"); pojoRoot.setColor(Color.BLUE); pojoRoot.setUser(new org.apache.servicecomb.foundation.protobuf.internal.model.User("name1")); pojoRoot.setSsMap(new LinkedHashMap<>()); pojoRoot.getSsMap().put("k1", "v1"); pojoRoot.getSsMap().put("k2", "v2"); pojoRoot.setSpMap(new LinkedHashMap<>()); pojoRoot.getSpMap().put("u1", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1")); pojoRoot.getSpMap().put("u2", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2")); pojoRoot.setInt32sPacked(Arrays.asList(10000, 20000, 30000)); pojoRoot.setInt64sPacked(Arrays.asList(10000L, 20000L, 30000L)); pojoRoot.setUint32sPacked(Arrays.asList(10000, 20000, 30000)); pojoRoot.setUint64sPacked(Arrays.asList(10000L, 20000L, 30000L)); pojoRoot.setSint32sPacked(Arrays.asList(10000, 20000, 30000)); pojoRoot.setSint64sPacked(Arrays.asList(10000L, 20000L, 30000L)); pojoRoot.setFixed32sPacked(Arrays.asList(10000, 20000, 30000)); pojoRoot.setFixed64sPacked(Arrays.asList(10000L, 20000L, 30000L)); pojoRoot.setSfixed32sPacked(Arrays.asList(10000, 20000, 30000)); pojoRoot.setSfixed64sPacked(Arrays.asList(10000L, 20000L, 30000L)); pojoRoot.setFloatsPacked(Arrays.asList((float) 10000, (float) 20000, (float) 30000)); pojoRoot.setDoublesPacked(Arrays.asList(10000.0, 20000.0, 30000.0)); pojoRoot.setBoolsPacked(Arrays.asList(true, false)); pojoRoot.setColorsPacked(Arrays.asList(Color.RED, Color.BLUE)); pojoRoot.setStrings(Arrays.asList("string value1", "string value2")); pojoRoot.setUsers(Arrays.asList( new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1"), new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2"), new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name3"), new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name4"))); builder.setInt32(10000) .setInt64(20000L) .setUint32(30000) .setUint64(40000L) .setSint32(50000) .setSint64(60000L) .setFixed32(70000) .setFixed64(80000L) .setSfixed32(90000) .setSfixed64(100000L) .setFloatValue((float) 10000) .setDoubleValue(20000.0) .setBool(true) .setObjInt32(10000) .setObjInt64(20000L) .setObjUint32(30000) .setObjUint64(40000L) .setObjSint32(50000) .setObjSint64(60000L) .setObjFixed32(70000) .setObjFixed64(80000L) .setObjSfixed32(90000) .setObjSfixed64(100000L) .setObjFloatValue((float) 10000) .setObjDoubleValue(20000.0) .setObjBool(true) .setString("string value") .setColorValue(2) .setUser(User.newBuilder().setName("name1").build()) .putSsMap("k1", "v1") .putSsMap("k2", "v2") .putSpMap("u1", User.newBuilder().setName("name1").build()) .putSpMap("u2", User.newBuilder().setName("name2").build()) .addAllInt32SPacked(Arrays.asList(10000, 20000, 30000)) .addAllInt64SPacked(Arrays.asList(10000L, 20000L, 30000L)) .addAllUint32SPacked(Arrays.asList(10000, 20000, 30000)) .addAllUint64SPacked(Arrays.asList(10000L, 20000L, 30000L)) .addAllSint32SPacked(Arrays.asList(10000, 20000, 30000)) .addAllSint64SPacked(Arrays.asList(10000L, 20000L, 30000L)) .addAllFixed32SPacked(Arrays.asList(10000, 20000, 30000)) .addAllFixed64SPacked(Arrays.asList(10000L, 20000L, 30000L)) .addAllSfixed32SPacked(Arrays.asList(10000, 20000, 30000)) .addAllSfixed64SPacked(Arrays.asList(10000L, 20000L, 30000L)) .addAllFloatsPacked(Arrays.asList((float) 10000, (float) 20000, (float) 30000)) .addAllDoublesPacked(Arrays.asList(10000.0, 20000.0, 30000.0)) .addAllBoolsPacked(Arrays.asList(true, false)) .addAllColorsPacked(Arrays.asList(ProtobufRoot.Color.RED, ProtobufRoot.Color.BLUE)) .addStrings("string value1") .addStrings("string value2") .addUsers(User.newBuilder().setName("name1").build()) .addUsers(User.newBuilder().setName("name2").build()) .addUsers(User.newBuilder().setName("name3").build()) .addUsers(User.newBuilder().setName("name4").build()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Pojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; public class Pojo extends TestBase { public Pojo() { pojoRoot.setUser(new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1")); builder.setUser(User.newBuilder().setName("name1").build()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/PojoList.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import java.util.Arrays; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; public class PojoList extends TestBase { public PojoList() { pojoRoot.setUsers(Arrays.asList( new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1"), new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2"))); builder.addUsers(User.newBuilder().setName("name1").build()) .addUsers(User.newBuilder().setName("name2").build()); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Scalars.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; public class Scalars extends TestBase { public Scalars() { pojoRoot.setInt32(10000); pojoRoot.setInt64(10000L); pojoRoot.setString("string value"); pojoRoot.setColor(Color.BLUE); builder.setInt32(10000) .setInt64(10000L) .setString("string value") .setColorValue(2); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/SimpleList.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.cases; import java.util.Arrays; import org.apache.servicecomb.foundation.protobuf.performance.TestBase; public class SimpleList extends TestBase { public SimpleList() { pojoRoot.setStrings(Arrays.asList("string value1", "string value2")); builder.addStrings("string value1") .addStrings("string value2"); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Jackson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.engine; import java.io.IOException; import java.net.URL; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; import org.apache.servicecomb.foundation.protobuf.internal.model.User; import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.dataformat.protobuf.ProtobufMapper; import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchemaLoader; public class Jackson implements ProtubufCodecEngine { @JsonIgnoreProperties({"ssMap", "spMap", "sint32Map", "any", "anys", "typeRecursive"}) interface RootMixin { } @JsonIgnoreProperties({"typeRecursive"}) interface UserMixin { } public static ObjectMapper jsonMapper = new ObjectMapper(); static ProtobufMapper protobufMapper = new ProtobufMapper(); static URL url = Jackson.class.getClassLoader().getResource("jacksonRoot.proto"); static ProtobufSchema protobufSchema; static { protobufMapper.addMixIn(Root.class, RootMixin.class); protobufMapper.addMixIn(User.class, UserMixin.class); try { protobufSchema = ProtobufSchemaLoader.std.load(url); } catch (IOException e) { throw new RuntimeException(e); } } static ObjectWriter writer = protobufMapper.writer(protobufSchema); static ObjectReader reader = protobufMapper.reader(protobufSchema).forType(Root.class); @Override public byte[] serialize(Object model) throws IOException { return writer.writeValueAsBytes(model); } @Override public Object deserialize(byte[] bytes) throws IOException { return reader.readValue(bytes); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protobuf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.engine; import java.io.IOException; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.Root; import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.Root.Builder; import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; public class Protobuf implements ProtubufCodecEngine { @Override public byte[] serialize(Object builder) { return ((Builder) builder).build().toByteArray(); } @Override public Object deserialize(byte[] bytes) throws IOException { Root.Builder builder = ProtobufRoot.Root.newBuilder().mergeFrom(bytes); builder.build(); return builder; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protostuff.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.engine; import java.io.IOException; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; import io.protostuff.ByteArrayInput; import io.protostuff.Input; import io.protostuff.LinkedBuffer; import io.protostuff.ProtobufOutput; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; public class Protostuff implements ProtubufCodecEngine { static Schema rootSchema = RuntimeSchema.createFrom(Root.class); @Override public byte[] serialize(Object model) throws IOException { LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); ProtobufOutput output = new ProtobufOutput(linkedBuffer); if (model != null) { rootSchema.writeTo(output, (Root) model); } return output.toByteArray(); } @Override public Object deserialize(byte[] bytes) throws IOException { Input input = new ByteArrayInput(bytes, false); Root result = new Root(); rootSchema.mergeFrom(input, result); return result; } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/SCB.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.engine; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.ProtoMapper; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.protobuf.internal.model.Root; public class SCB { static ProtoMapperFactory factory = new ProtoMapperFactory(); static ProtoMapper protoMapper = factory.createFromName("protobufRoot.proto"); static RootSerializer serializer = protoMapper.createRootSerializer("Root", Root.class); static RootDeserializer deserializer = protoMapper.createRootDeserializer("Root", Root.class); static RootDeserializer> mapDeserializer = protoMapper.createRootDeserializer("Root", Map.class); } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbStrong.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.engine; import java.io.IOException; import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; public class ScbStrong implements ProtubufCodecEngine { @Override public byte[] serialize(Object model) throws IOException { return SCB.serializer.serialize(model); } @Override public Object deserialize(byte[] bytes) throws IOException { return SCB.deserializer.deserialize(bytes); } } ================================================ FILE: foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbWeak.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.protobuf.performance.engine; import java.io.IOException; import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; public class ScbWeak implements ProtubufCodecEngine { @Override public byte[] serialize(Object model) throws IOException { return SCB.serializer.serialize(model); } @Override public Object deserialize(byte[] bytes) throws IOException { return SCB.mapDeserializer.deserialize(bytes); } } ================================================ FILE: foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto2"; package org.apache.servicecomb.foundation.protobuf.internal.model; message Root { optional int32 int32 = 1; optional int64 int64 = 2; optional uint32 uint32 = 3; optional uint64 uint64 = 4; optional sint32 sint32 = 5; optional sint64 sint64 = 6; optional fixed32 fixed32 = 7; optional fixed64 fixed64 = 8; optional sfixed32 sfixed32 = 9; optional sfixed64 sfixed64 = 10; optional float floatValue = 11; optional double doubleValue = 12; optional bool bool = 13; optional int32 objInt32 = 20; optional int64 objInt64 = 21; optional uint32 objUint32 = 22; optional uint64 objUint64 = 23; optional sint32 objSint32 = 24; optional sint64 objSint64 = 25; optional fixed32 objFixed32 = 26; optional fixed64 objFixed64 = 27; optional sfixed32 objSfixed32 = 28; optional sfixed64 objSfixed64 = 29; optional float objFloatValue = 30; optional double objDoubleValue = 31; optional bool objBool = 32; optional string string = 40; optional bytes bytes = 41; optional Color color = 42; optional User user = 43; repeated int32 int32sPacked = 70; repeated int64 int64sPacked = 71; repeated uint32 uint32sPacked = 72; repeated uint64 uint64sPacked = 73; repeated sint32 sint32sPacked = 74; repeated sint64 sint64sPacked = 75; repeated fixed32 fixed32sPacked = 76; repeated fixed64 fixed64sPacked = 77; repeated sfixed32 sfixed32sPacked = 78; repeated sfixed64 sfixed64sPacked = 79; repeated float floatsPacked = 80; repeated double doublesPacked = 81; repeated bool boolsPacked = 82; repeated Color colorsPacked = 83; repeated int32 int32sNotPacked = 90 [packed = false]; repeated int64 int64sNotPacked = 91 [packed = false]; repeated uint32 uint32sNotPacked = 92 [packed = false]; repeated uint64 uint64sNotPacked = 93 [packed = false]; repeated sint32 sint32sNotPacked = 94 [packed = false]; repeated sint64 sint64sNotPacked = 95 [packed = false]; repeated fixed32 fixed32sNotPacked = 96 [packed = false]; repeated fixed64 fixed64sNotPacked = 97 [packed = false]; repeated sfixed32 sfixed32sNotPacked = 98 [packed = false]; repeated sfixed64 sfixed64sNotPacked = 99 [packed = false]; repeated float floatsNotPacked = 100 [packed = false]; repeated double doublesNotPacked = 101 [packed = false]; repeated bool boolsNotPacked = 102 [packed = false]; repeated Color colorsNotPacked = 103 [packed = false]; repeated string strings = 110; repeated bytes bytess = 111; repeated User users = 112; } enum Color { RED = 0; YELLOW = 1; BLUE = 2; } message User { optional string name = 1; } ================================================ FILE: foundations/foundation-protobuf/src/test/resources/method.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; //@WrapArguments message RequestWrap { bool boolValue = 1; repeated int32 iArr = 2; repeated User user = 3; } message Request { bool boolValue = 1; repeated int32 iArr = 2; repeated User user = 3; } //@WrapProperty message ResponseWrap { repeated int32 value = 1; } message Response { repeated int32 value = 1; } message User { string name = 1; repeated User friends = 2; } ================================================ FILE: foundations/foundation-protobuf/src/test/resources/model.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; message PojoModel { repeated PojoListUser listListUser = 1; repeated PojoMapUser listMapUser = 2; map mapListUser = 3; map mapMapUser = 4; repeated PojoListListUser listListListUser = 5; repeated PojoListMapUser listListMapUser = 6; repeated PojoMapListUser listMapListUser = 7; repeated PojoMapMapUser listMapMapUser = 8; map mapListListUser = 9; map mapListMapUser = 10; map mapMapListUser = 11; map mapMapMapUser = 12; } message ProtoModel { repeated ProtoListUser listListUser = 1; repeated ProtoMapUser listMapUser = 2; map mapListUser = 3; map mapMapUser = 4; repeated ProtoListListUser listListListUser = 5; repeated ProtoListMapUser listListMapUser = 6; repeated ProtoMapListUser listMapListUser = 7; repeated ProtoMapMapUser listMapMapUser = 8; map mapListListUser = 9; map mapListMapUser = 10; map mapMapListUser = 11; map mapMapMapUser = 12; } //@WrapProperty message PojoListListUser { repeated PojoListUser value = 1; } //@WrapProperty message PojoListMapUser { repeated PojoMapUser value = 1; } //@WrapProperty message PojoMapListUser { map value = 1; } //@WrapProperty message PojoMapMapUser { map value = 1; } //@WrapProperty message PojoListUser { repeated User value = 1; } //@WrapProperty message PojoMapUser { map value = 1; } message User { string name = 1; } message ProtoListListUser { repeated ProtoListUser value = 1; } message ProtoListMapUser { repeated ProtoMapUser value = 1; } message ProtoMapListUser { map value = 1; } message ProtoMapMapUser { map value = 1; } message ProtoListUser { repeated User value = 1; } message ProtoMapUser { map value = 1; } ================================================ FILE: foundations/foundation-protobuf/src/test/resources/protobufRoot.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; import "google/protobuf/any.proto"; package org.apache.servicecomb.foundation.protobuf.internal.model; option java_package = "org.apache.servicecomb.foundation.protobuf.internal.model"; option java_outer_classname = "ProtobufRoot"; message Root { int32 int32 = 1; int64 int64 = 2; uint32 uint32 = 3; uint64 uint64 = 4; sint32 sint32 = 5; sint64 sint64 = 6; fixed32 fixed32 = 7; fixed64 fixed64 = 8; sfixed32 sfixed32 = 9; sfixed64 sfixed64 = 10; float floatValue = 11; double doubleValue = 12; bool bool = 13; int32 objInt32 = 20; int64 objInt64 = 21; uint32 objUint32 = 22; uint64 objUint64 = 23; sint32 objSint32 = 24; sint64 objSint64 = 25; fixed32 objFixed32 = 26; fixed64 objFixed64 = 27; sfixed32 objSfixed32 = 28; sfixed64 objSfixed64 = 29; float objFloatValue = 30; double objDoubleValue = 31; bool objBool = 32; string string = 40; bytes bytes = 41; Color color = 42; User user = 43; Root typeRecursive = 44; google.protobuf.Any any = 50; repeated google.protobuf.Any anys = 51; map ssMap = 60; map sint32Map = 61; map spMap = 62; repeated int32 int32sPacked = 70; repeated int64 int64sPacked = 71; repeated uint32 uint32sPacked = 72; repeated uint64 uint64sPacked = 73; repeated sint32 sint32sPacked = 74; repeated sint64 sint64sPacked = 75; repeated fixed32 fixed32sPacked = 76; repeated fixed64 fixed64sPacked = 77; repeated sfixed32 sfixed32sPacked = 78; repeated sfixed64 sfixed64sPacked = 79; repeated float floatsPacked = 80; repeated double doublesPacked = 81; repeated bool boolsPacked = 82; repeated Color colorsPacked = 83; repeated int32 int32sNotPacked = 90 [packed = false]; repeated int64 int64sNotPacked = 91 [packed = false]; repeated uint32 uint32sNotPacked = 92 [packed = false]; repeated uint64 uint64sNotPacked = 93 [packed = false]; repeated sint32 sint32sNotPacked = 94 [packed = false]; repeated sint64 sint64sNotPacked = 95 [packed = false]; repeated fixed32 fixed32sNotPacked = 96 [packed = false]; repeated fixed64 fixed64sNotPacked = 97 [packed = false]; repeated sfixed32 sfixed32sNotPacked = 98 [packed = false]; repeated sfixed64 sfixed64sNotPacked = 99 [packed = false]; repeated float floatsNotPacked = 100 [packed = false]; repeated double doublesNotPacked = 101 [packed = false]; repeated bool boolsNotPacked = 102 [packed = false]; repeated Color colorsNotPacked = 103 [packed = false]; repeated string strings = 110; repeated bytes bytess = 111; repeated User users = 112; } enum Color { RED = 0; YELLOW = 1; BLUE = 2; } message User { string name = 1; Root typeRecursive = 2; } ================================================ FILE: foundations/foundation-registry/pom.xml ================================================ foundations org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 foundation-registry Java Chassis::Foundations::Registry org.apache.servicecomb foundation-config org.apache.servicecomb foundation-vertx org.apache.servicecomb swagger-invocation-core org.apache.servicecomb foundation-test-scaffolding org.mockito mockito-inline test ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.foundation.common.NamedThreadFactory; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.LambdaUtils; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.api.LifeCycle; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.discovery.InstancePing; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.PingStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; public class DiscoveryManager implements LifeCycle { public interface InstanceChangeListener { void onInstancesChanged(String registryName, String application, String serviceName, List instances); } private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryManager.class); private final ScheduledExecutorService task; private final List> discoveryList; private final InstancePing ping; private final HealthCheckTask healthCheckTask = new HealthCheckTask(); // application:serviceName:instanceId private final Map>> allInstances = new ConcurrentHashMapEx<>(); // application:serviceName private final Map> versionedCache = new ConcurrentHashMapEx<>(); private final Object cacheLock = new Object(); private final List instanceChangeListeners = new ArrayList<>(); public DiscoveryManager(List> discoveryList, List pings) { this.discoveryList = discoveryList; for (Discovery discovery : this.discoveryList) { discovery.setInstanceChangedListener(this::onInstancesChanged); } this.ping = pings.get(0); task = Executors.newScheduledThreadPool(1, new NamedThreadFactory("discovery-manager-task")); } public Discovery getPrimaryDiscovery() { return this.discoveryList.get(0); } private void onInstancesChanged(String application, String serviceName, List instances) { onInstancesChanged(null, application, serviceName, instances); } private void onInstancesChanged(String registryName, String application, String serviceName, List instances) { Map statefulInstances = allInstances.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, key -> new ConcurrentHashMapEx<>()); for (StatefulDiscoveryInstance statefulInstance : statefulInstances.values()) { if (registryName == null || registryName.equals(statefulInstance.getRegistryName())) { if (!instances.contains(statefulInstance)) { statefulInstance.setPingTime(0); statefulInstance.setHistoryStatus(HistoryStatus.HISTORY); } } } for (DiscoveryInstance instance : instances) { StatefulDiscoveryInstance target = new StatefulDiscoveryInstance(instance); StatefulDiscoveryInstance origin = statefulInstances.get(instance.getInstanceId()); if (origin == null) { statefulInstances.put(instance.getInstanceId(), target); continue; } target.setPingTime(origin.getPingTime()); target.setPingStatus(origin.getPingStatus()); target.setIsolateDuration(origin.getIsolateDuration()); target.setIsolationStatus(origin.getIsolationStatus()); statefulInstances.put(instance.getInstanceId(), target); } StringBuilder instanceInfo = new StringBuilder(); for (DiscoveryInstance instance : instances) { instanceInfo.append("{") .append(instance.getInstanceId()).append(",") .append(instance.getStatus()).append(",") .append(instance.getEndpoints()).append(",") .append(instance.getRegistryName()) .append("}"); } LOGGER.info("Applying new instance list for {}/{}/{}. Endpoints {}", application, serviceName, instances.size(), instanceInfo); rebuildVersionCache(application, serviceName); for (InstanceChangeListener listener : this.instanceChangeListeners) { listener.onInstancesChanged(registryName, application, serviceName, instances); } } public void addInstanceChangeListener(InstanceChangeListener instanceChangeListener) { this.instanceChangeListeners.add(instanceChangeListener); } public void onInstanceIsolated(DiscoveryInstance instance, long isolateDuration) { Map statefulInstances = allInstances.computeIfAbsent( instance.getApplication(), key -> new ConcurrentHashMapEx<>()).computeIfAbsent(instance.getServiceName(), key -> new ConcurrentHashMapEx<>()); StatefulDiscoveryInstance target = statefulInstances.get(instance.getInstanceId()); if (target == null) { return; } target.setIsolatedTime(System.currentTimeMillis()); target.setIsolateDuration(isolateDuration); if (target.getIsolationStatus() != IsolationStatus.ISOLATED) { target.setIsolationStatus(IsolationStatus.ISOLATED); rebuildVersionCache(instance.getApplication(), instance.getServiceName()); } LOGGER.warn("Isolated instance {}/{}/{}, time {}/{}", instance.getApplication(), instance.getServiceName(), instance.getInstanceId(), target.getIsolatedTime(), target.getIsolateDuration()); } private void rebuildVersionCache(String application, String serviceName) { Map caches = versionedCache.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()); caches.put(serviceName, calcAvailableInstance(application, serviceName)); } private VersionedCache calcAvailableInstance(String application, String serviceName) { Map statefulInstances = allInstances.computeIfAbsent( application, key -> new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, key -> new ConcurrentHashMapEx<>()); List result = new ArrayList<>(); for (StatefulDiscoveryInstance instance : statefulInstances.values()) { if (instance.getHistoryStatus() == HistoryStatus.CURRENT) { result.add(instance); continue; } if (instance.getHistoryStatus() == HistoryStatus.HISTORY && instance.getStatus() == MicroserviceInstanceStatus.UP && instance.getPingStatus() == PingStatus.OK && instance.getIsolationStatus() == IsolationStatus.NORMAL) { result.add(instance); } } StringBuilder instanceInfo = new StringBuilder(); for (StatefulDiscoveryInstance instance : result) { instanceInfo.append("{") .append(instance.getInstanceId()).append(",") .append(instance.getHistoryStatus()).append(",") .append(instance.getStatus()).append(",") .append(instance.getPingStatus()).append(",") .append(instance.getIsolationStatus()).append(",") .append(instance.getEndpoints()).append(",") .append(instance.getRegistryName()) .append("}"); } LOGGER.info("Rebuild cached instance list for {}/{}/{}. Endpoints {}", application, serviceName, result.size(), instanceInfo); return new VersionedCache() .name(application + ":" + serviceName) .autoCacheVersion() .data(result); } public VersionedCache getOrCreateVersionedCache(String application, String serviceName) { Map caches = versionedCache.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()); VersionedCache cache = caches.get(serviceName); if (cache == null) { synchronized (cacheLock) { cache = caches.get(serviceName); if (cache != null) { return cache; } List instances = findServiceInstances(application, serviceName); onInstancesChanged(application, serviceName, instances); return versionedCache.get(application).get(serviceName); } } return cache; } public List findServiceInstances(String application, String serviceName) { List result = new ArrayList<>(); for (Discovery discovery : discoveryList) { if (!discovery.enabled() || !discovery.enabled(application, serviceName)) { continue; } List temp = discovery.findServiceInstances(application, serviceName); if (CollectionUtils.isEmpty(temp)) { continue; } result.addAll(temp); } return result; } @Override public void destroy() { discoveryList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); task.shutdownNow(); } @Override public void run() { discoveryList.forEach(LifeCycle::run); task.schedule(healthCheckTask, 3, TimeUnit.SECONDS); } @Override public void init() { discoveryList.forEach(LifeCycle::init); } public String info() { StringBuilder result = new StringBuilder(); AtomicBoolean first = new AtomicBoolean(true); discoveryList.forEach(discovery -> { if (first.getAndSet(false)) { result.append("Discovery implementations:\n"); } result.append(" name:").append(discovery.name()).append("\n"); }); return result.toString(); } class HealthCheckTask implements Runnable { @Override public void run() { try { Map>> removed = new HashMap<>(); for (Entry>> apps : DiscoveryManager.this.allInstances.entrySet()) { for (Entry> services : apps.getValue().entrySet()) { boolean changed = false; for (StatefulDiscoveryInstance instance : services.getValue().values()) { // check isolated time if (instance.getIsolationStatus() == IsolationStatus.ISOLATED && instance.getIsolatedTime() + instance.getIsolateDuration() < System.currentTimeMillis()) { instance.setIsolationStatus(IsolationStatus.NORMAL); changed = true; } // check ping status if (System.currentTimeMillis() - instance.getPingTime() > 60_000L) { boolean pingResult = DiscoveryManager.this.ping.ping(instance); if (pingResult && instance.getPingStatus() != PingStatus.OK) { instance.setPingStatus(PingStatus.OK); changed = true; } else if (!pingResult && instance.getPingStatus() != PingStatus.FAIL) { instance.setPingStatus(PingStatus.FAIL); changed = true; } instance.setPingTime(System.currentTimeMillis()); } // check unused if (instance.getHistoryStatus() == HistoryStatus.HISTORY) { if (instance.getStatus() != MicroserviceInstanceStatus.UP || instance.getPingStatus() == PingStatus.FAIL || instance.getIsolationStatus() == IsolationStatus.ISOLATED) { removed.computeIfAbsent(apps.getKey(), k -> new HashMap<>()) .computeIfAbsent(services.getKey(), k -> new ArrayList<>()).add(instance.getInstanceId()); LOGGER.info("Remove instance {}/{}/{}/{}/{}/{}/{}/{}", apps.getKey(), services.getKey(), instance.getRegistryName(), instance.getInstanceId(), instance.getHistoryStatus(), instance.getStatus(), instance.getPingStatus(), instance.getIsolationStatus()); changed = true; } } } if (changed) { rebuildVersionCache(apps.getKey(), services.getKey()); } } } // remove unused for (Entry>> apps : removed.entrySet()) { for (Entry> services : apps.getValue().entrySet()) { for (String instance : services.getValue()) { DiscoveryManager.this.allInstances.get(apps.getKey()).get(services.getKey()).remove(instance); } } } } catch (Throwable e) { LOGGER.error("discovery manager task error. ", e); } finally { DiscoveryManager.this.task.schedule(this, 3, TimeUnit.SECONDS); } } } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RegistrationId { private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationId.class); private final String instanceId; public RegistrationId() { this.instanceId = buildInstanceId(); LOGGER.info("initialized global registration id {}", this.instanceId); } public String getInstanceId() { return instanceId; } private static String buildInstanceId() { return UUID.randomUUID().toString(); } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.utils.LambdaUtils; import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.registry.api.LifeCycle; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.springframework.util.CollectionUtils; import io.vertx.core.json.jackson.JacksonFactory; public class RegistrationManager { private final List> registrationList; public RegistrationManager(List> registrationList) { if (registrationList == null) { this.registrationList = Collections.emptyList(); return; } this.registrationList = registrationList.stream().filter(SPIEnabled::enabled).collect(Collectors.toList()); } /** * For internal use. Get instance id from registry name. If not exists, return empty. */ public String getInstanceId(String registryName) { if (CollectionUtils.isEmpty(registrationList)) { return ""; } Optional> registration = registrationList.stream().filter(r -> registryName.equals(r.name())).collect(Collectors.toList()) .stream().findFirst(); if (!registration.isPresent()) { return ""; } return registration.get().getMicroserviceInstance().getInstanceId(); } /** * For internal use. Get service id from registry name. If not exists, return empty. */ public String getServiceId(String registryName) { if (CollectionUtils.isEmpty(registrationList)) { return ""; } Optional> registration = registrationList.stream().filter(r -> registryName.equals(r.name())).collect(Collectors.toList()) .stream().findFirst(); if (!registration.isPresent()) { return ""; } return registration.get().getMicroserviceInstance().getServiceId(); } public Registration getPrimaryRegistration() { return registrationList.get(0); } public void updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { registrationList .forEach(registration -> registration.updateMicroserviceInstanceStatus(status)); } public void addProperty(String key, String value) { registrationList .forEach(registration -> registration.addProperty(key, value)); } public void addSchema(String schemaId, String content) { registrationList .forEach(registration -> registration.addSchema(schemaId, content)); } public void addEndpoint(String endpoint) { registrationList .forEach(registration -> registration.addEndpoint(endpoint)); } public void destroy() { registrationList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); } public void run() { registrationList.forEach(LifeCycle::run); } public void init() { registrationList.forEach(LifeCycle::init); } public String info() { StringBuilder result = new StringBuilder(); AtomicBoolean first = new AtomicBoolean(true); registrationList.forEach(registration -> { if (first.getAndSet(false)) { result.append("App ID: ").append(registration.getMicroserviceInstance().getApplication()).append("\n"); result.append("Service Name: ").append(registration.getMicroserviceInstance().getServiceName()).append("\n"); result.append("Version: ").append(registration.getMicroserviceInstance().getVersion()).append("\n"); result.append("Environment: ").append(registration.getMicroserviceInstance().getEnvironment()).append("\n"); result.append("Endpoints: ").append(getEndpoints(registration.getMicroserviceInstance().getEndpoints())) .append("\n"); result.append("Registration implementations:\n"); } result.append(" name:").append(registration.name()).append("\n"); result.append(" Instance ID: ").append(registration.getMicroserviceInstance().getInstanceId()).append("\n"); }); return result.toString(); } private String getEndpoints(List endpoints) { return JacksonFactory.CODEC.toString(endpoints); } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry; import java.util.List; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.api.Registration; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.registry.discovery.InstancePing; import org.apache.servicecomb.registry.discovery.InstanceStatusDiscoveryFilter; import org.apache.servicecomb.registry.discovery.MicroserviceInstanceCache; import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @SuppressWarnings("unused") public class RegistryConfiguration { @Bean public RegistrationManager scbRegistrationManager( List> registrationList) { return new RegistrationManager(registrationList); } @Bean public TelnetInstancePing scbTelnetInstancePing() { return new TelnetInstancePing(); } @Bean public DiscoveryManager scbDiscoveryManager( List> discoveryList, List pingList) { return new DiscoveryManager(discoveryList, pingList); } @Bean public DiscoveryTree scbDiscoveryTree(DiscoveryManager discoveryManager) { return new DiscoveryTree(discoveryManager); } @Bean public InstanceStatusDiscoveryFilter scbInstanceStatusDiscoveryFilter() { return new InstanceStatusDiscoveryFilter(); } @Bean public MicroserviceInstanceCache scbMicroserviceInstanceCache(DiscoveryManager discoveryManager) { return new MicroserviceInstanceCache(discoveryManager); } @Bean public RegistrationId scbRegistrationId() { return new RegistrationId(); } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/AbstractDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; public abstract class AbstractDiscoveryInstance implements DiscoveryInstance { @Override public int hashCode() { return getInstanceId().hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof DiscoveryInstance) { return getInstanceId().equals(((DiscoveryInstance) obj).getInstanceId()); } return false; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DataCenterInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; public class DataCenterInfo { private String name; private String region; private String availableZone; public DataCenterInfo() { } public DataCenterInfo(String name, String region, String availableZone) { this.name = name; this.region = region; this.availableZone = availableZone; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRegion() { return region; } public void setRegion(String region) { this.region = region; } public String getAvailableZone() { return availableZone; } public void setAvailableZone(String availableZone) { this.availableZone = availableZone; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; import java.util.List; import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.foundation.common.utils.SPIOrder; /** * This is the core service discovery interface.
*/ public interface Discovery extends SPIEnabled, SPIOrder, LifeCycle { interface InstanceChangedListener { /** * Called by Discovery Implementations when instance list changed. * @param registryName Name of the calling discovery implementation * @param application Microservice application * @param serviceName Microservice name * @param updatedInstances The latest updated instances. */ void onInstanceChanged(String registryName, String application, String serviceName, List updatedInstances); } String name(); /** * If this implementation enabled for this microservice. */ boolean enabled(String application, String serviceName); /** * Find all instances. * * Life Cycle:This method is called anytime after run. * * @param application application * @param serviceName microservice name * @return all instances match the criteria. */ List findServiceInstances(String application, String serviceName); /** * Find all services of this application. * * @param application application * @return all services match the criteria. */ List findServices(String application); /** * Discovery can call InstanceChangedListener when instance get changed. */ void setInstanceChangedListener(InstanceChangedListener instanceChangedListener); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; /** * Microserivce instance discovery object. */ public interface DiscoveryInstance extends MicroserviceInstance { /** * Microservice instance status. */ MicroserviceInstanceStatus getStatus(); /** * This name of Discovery Implementation. */ String getRegistryName(); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/LifeCycle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; public interface LifeCycle { void init(); void run(); void destroy(); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; import java.util.List; import java.util.Map; /** * Standard information used for microservice instance registration and discovery. */ public interface MicroserviceInstance { /** * Environment(Required): Used for logic separation of microservice instance. Only * microservice instance with same environment can discovery each other. */ String getEnvironment(); /** * Application(Required): Used for logic separation of microservice instance. Only * microservice instance with same application can discovery each other. */ String getApplication(); /** * Service Name(Required): Unique identifier for microservice. */ String getServiceName(); /** * Service Name Alias(Optional): Unique identifier for microservice. * This alias is used by registry implementation to support rename * of a microservice, e.g. old consumers use old service name can * find a renamed microservice service. */ String getAlias(); /** * Service Version(Required): version of this microservice. */ String getVersion(); /** * Data center info(Optional). */ DataCenterInfo getDataCenterInfo(); /** * Service Description(Optional) */ String getDescription(); /** * Service Properties(Optional) */ Map getProperties(); /** * Service Schemas(Optional): Open API information. */ Map getSchemas(); /** * Service endpoints(Optional). */ List getEndpoints(); /** * Microservice instance id(Required). This id can be generated when microservice instance is starting * or assigned by registry implementation. * * When microservice instance is restarted, this id should be changed. */ String getInstanceId(); /** * Microservice service id(Optional). This is used for service center, other implementations may not * support service id. */ default String getServiceId() { return ""; } /** * Service status(Required): status of this microservice. * */ MicroserviceInstanceStatus getStatus(); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstanceStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; public enum MicroserviceInstanceStatus { STARTING, TESTING, UP, DOWN, UNKNOWN } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceKey.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Created by on 2017/1/9. */ @JsonIgnoreProperties(ignoreUnknown = true) public class MicroserviceKey { private String tenant; private String appId; private String serviceName; private String version; private String stage; public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getStage() { return stage; } public void setStage(String stage) { this.stage = stage; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/PropertyExtended.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; import java.util.Map; public interface PropertyExtended { Map getExtendedProperties(); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.foundation.common.utils.SPIOrder; /** * This is the core service registration interface. */ public interface Registration extends SPIEnabled, SPIOrder, LifeCycle { String name(); /** * get MicroserviceInstance *

* Life Cycle:This method is called anytime after run. */ R getMicroserviceInstance(); /** * update MicroserviceInstance status *

* Life Cycle:This method is called anytime after run. */ boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status); /** * adding schemas to Microservice *

* Life Cycle:This method is called after init and before run. */ void addSchema(String schemaId, String content); /** * adding endpoints to MicroserviceInstance *

* Life Cycle:This method is called after init and before run. */ void addEndpoint(String endpoint); /** * adding property to MicroserviceInstance *

* Life Cycle:This method is called after init and before run. */ void addProperty(String key, String value); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.api; /** * Microserivce instance registration object. */ public interface RegistrationInstance extends MicroserviceInstance { } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.definition; public interface DefinitionConst { // Whether to allow cross-app calls to me String CONFIG_ALLOW_CROSS_APP_KEY = "allowCrossApp"; String DEFAULT_STAGE = "prod"; String VERSION_RULE_LATEST = "latest"; String VERSION_RULE_ALL = "0.0.0.0+"; String APP_SERVICE_SEPARATOR = ":"; String URL_PREFIX = "urlPrefix"; String INSTANCE_PUBKEY_PRO = "publickey"; String REGISTER_URL_PREFIX = "servicecomb.registry.registerUrlPrefix"; } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/MicroserviceNameParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.definition; /** * Parse application and microservice from join name. * * 1. microserviceName * 2. application:microserviceName */ public class MicroserviceNameParser { private String appId; private String microserviceName; public MicroserviceNameParser(String defaultAppId, String microserviceName) { parseMicroserviceName(defaultAppId, microserviceName); } private void parseMicroserviceName(String defaultAppId, String microserviceName) { int idxAt = microserviceName.indexOf(DefinitionConst.APP_SERVICE_SEPARATOR); if (idxAt == -1) { this.appId = defaultAppId; this.microserviceName = microserviceName; return; } this.appId = microserviceName.substring(0, idxAt); this.microserviceName = microserviceName.substring(idxAt + 1); } public String getAppId() { return appId; } public String getMicroserviceName() { return microserviceName; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.ArrayList; import org.apache.servicecomb.config.DynamicProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public abstract class AbstractDiscoveryFilter implements DiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDiscoveryFilter.class); protected Boolean enabled; protected DynamicProperties dynamicProperties; @Autowired public void setDynamicProperties(DynamicProperties dynamicProperties) { this.dynamicProperties = dynamicProperties; } @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { if (!parent.childrenInited()) { synchronized (parent) { if (!parent.childrenInited()) { init(context, parent); parent.childrenInited(true); } } } String childName = findChildName(context, parent); DiscoveryTreeNode node = parent.child(childName); if (node == null) { LOGGER.warn("discovery filter {}/{} return null.", this.getClass().getSimpleName(), childName); return new DiscoveryTreeNode().subName(parent, "empty").data(new ArrayList<>()); } return node; } protected abstract void init(DiscoveryContext context, DiscoveryTreeNode parent); protected abstract String findChildName(DiscoveryContext context, DiscoveryTreeNode parent); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractEndpointDiscoveryFilter implements DiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractEndpointDiscoveryFilter.class); private static final String ALL_TRANSPORT = ""; private static final String WEBSOCKET_TRANSPORT = "websocket"; private static final String REST_TRANSPORT = "rest"; @Override public boolean isGroupingFilter() { return true; } @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { String expectTransportName = findTransportName(context, parent); return parent.children() .computeIfAbsent(expectTransportName, etn -> createDiscoveryTreeNode(expectTransportName, context, parent)); } protected DiscoveryTreeNode createDiscoveryTreeNode(String expectTransportName, DiscoveryContext context, DiscoveryTreeNode parent) { List endpoints = new ArrayList<>(); List instances = parent.data(); for (StatefulDiscoveryInstance instance : instances) { for (String endpoint : instance.getEndpoints()) { try { URIEndpointObject endpointObject = new URIEndpointObject(endpoint); String transPortName = endpointObject.getSchema(); if (endpointObject.isWebsocketEnabled() && WEBSOCKET_TRANSPORT.equals(expectTransportName)) { transPortName = WEBSOCKET_TRANSPORT; } if (!isTransportNameMatch(transPortName, expectTransportName)) { continue; } Object objEndpoint = createEndpoint(context, transPortName, endpoint, instance); if (objEndpoint == null) { continue; } endpoints.add(objEndpoint); } catch (Exception e) { LOGGER.warn("unrecognized address find, ignore {}.", endpoint); } } } return new DiscoveryTreeNode() .subName(parent, expectTransportName) .data(endpoints); } protected boolean isTransportNameMatch(String transportName, String expectTransportName) { if (ALL_TRANSPORT.equals(expectTransportName)) { return true; } return transportName.equals(expectTransportName); } protected abstract String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent); protected abstract Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, StatefulDiscoveryInstance instance); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractGroupDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; public abstract class AbstractGroupDiscoveryFilter extends AbstractDiscoveryFilter { @Override public boolean isGroupingFilter() { return true; } protected abstract String groupsSizeParameter(); protected abstract String contextParameter(); protected abstract String groupPrefix(); @Override protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { Integer level = context.getContextParameter(contextParameter()); Integer groups = parent.attribute(groupsSizeParameter()); String group; if (level == null) { group = groupPrefix() + 1; if (groups > 1) { context.pushRerunFilter(); context.putContextParameter(contextParameter(), 1); } return group; } level = level + 1; group = groupPrefix() + level; if (level < groups) { context.pushRerunFilter(); context.putContextParameter(contextParameter(), level); } return group; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.HashMap; import java.util.Map; import java.util.Stack; public class DiscoveryContext { private Object inputParameters; private final Map contextParameters = new HashMap<>(); // some filter support rerun logic, eg:ZoneAware // instances grouping to self zone, other zone, and so on // first try self zone, after other filter(Isolation Filter), no instances are available // then try other zone private final Stack rerunStack = new Stack<>(); private DiscoveryTreeNode currentNode; @SuppressWarnings("unchecked") public T getInputParameters() { return (T) inputParameters; } public void setInputParameters(Object inputParameters) { this.inputParameters = inputParameters; } @SuppressWarnings("unchecked") public T getContextParameter(String name) { return (T) contextParameters.get(name); } public void putContextParameter(String name, Object value) { contextParameters.put(name, value); } public void setCurrentNode(DiscoveryTreeNode node) { this.currentNode = node; } public void pushRerunFilter() { rerunStack.push(currentNode); } public DiscoveryTreeNode popRerunFilter() { if (rerunStack.isEmpty()) { return null; } return rerunStack.pop(); } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import org.springframework.core.Ordered; /** * Server list filters for DiscoveryTree. * * Implementation Notice: DiscoveryFilter is initialized using bean and instance shared for all * microservices. If implementation has states, can put the states to DiscoveryContext or parent DiscoveryTreeNode. */ public interface DiscoveryFilter extends Ordered { default boolean enabled() { return true; } /** * grouping filter, means grouping instances to some groups * eg: operation/ZoneAware/transport */ default boolean isGroupingFilter() { return false; } DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.registry.DiscoveryManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; /** *
 * DiscoveryTree is used to:
 * 1.get all instances by app/microserviceName
 * 2.filter all instances set, and output another set, the output set is instance or something else, this depend on filter set
 *
 * DiscoveryFilter have different types:
 * 1.normal filter: just filter input set
 * 2.grouping filter: will split input set to groups
 * 3.convert filter: eg: convert from instance to endpoint
 * different types can composite in one filter
 *
 * features:
 * 1.if some filter output set is empty, DiscoveryTree can support try refilter logic
 *   eg: if there is no available instances in self AZ, can refilter in other AZ
 * 2.every filter must try to cache result, avoid calculate every time.
 *
 * usage:
 * 1.declare a field: DiscoveryTree discoveryTree = new DiscoveryTree();
 * 2.initialize:
 *     discoveryTree.loadFromSPI(DiscoveryFilter.class);
 *     // add filters by your requirement
 *     discoveryTree.addFilter(new EndpointDiscoveryFilter());
 *     discoveryTree.sort();
 * 3.filter for a invocation:
 *     DiscoveryContext context = new DiscoveryContext();
 *     context.setInputParameters(invocation);
 *     VersionedCache endpointsVersionedCache = discoveryTree.discovery(context,
 *         invocation.getAppId(),
 *         invocation.getMicroserviceName(),
 *         invocation.getMicroserviceVersionRule());
 *     if (endpointsVersionedCache.isEmpty()) {
 *       // 404 not found logic
 *       ......
 *       return;
 *     }
 *
 *     // result is endpoints or something else, which is depends on your filter set
 *     List<Endpoint> endpoints = endpointsVersionedCache.data();
 *
*/ public class DiscoveryTree { private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryTree.class); private final Map> root = new ConcurrentHashMapEx<>(); private final Object lock = new Object(); private List filters = Collections.emptyList(); private final DiscoveryManager discoveryManager; public DiscoveryTree(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Autowired public void setDiscoveryFilters(List filters) { this.filters = filters; log(); } private void log() { StringBuilder sb = new StringBuilder(); sb.append("DiscoveryFilters(name, enabled, order, group):"); for (DiscoveryFilter filter : filters) { sb.append("(").append(filter.getClass().getName()).append(",") .append(filter.enabled()).append(",").append(filter.getOrder()).append(",") .append(filter.isGroupingFilter()).append(")"); } LOGGER.info(sb.toString()); } boolean isMatch(VersionedCache existing, VersionedCache inputCache) { return existing != null && existing.isSameVersion(inputCache); } boolean isExpired(VersionedCache existing, VersionedCache inputCache) { return existing == null || existing.isExpired(inputCache); } public DiscoveryTreeNode discovery(DiscoveryContext context, String appId, String microserviceName) { VersionedCache instanceVersionedCache = this.discoveryManager.getOrCreateVersionedCache(appId, microserviceName); return discovery(appId, microserviceName, context, instanceVersionedCache); } DiscoveryTreeNode discovery(String appId, String microserviceName, DiscoveryContext context, VersionedCache inputCache) { DiscoveryTreeNode tmpRoot = getOrCreateRoot(appId, microserviceName, inputCache); DiscoveryTreeNode parent = tmpRoot.children() .computeIfAbsent(inputCache.name(), name -> new DiscoveryTreeNode().fromCache(inputCache)); return doDiscovery(context, parent); } protected DiscoveryTreeNode getOrCreateRoot(String appId, String microserviceName, VersionedCache inputCache) { DiscoveryTreeNode tmpRoot = root.computeIfAbsent(appId, k -> new ConcurrentHashMapEx<>()).get(microserviceName); if (isMatch(tmpRoot, inputCache)) { return tmpRoot; } synchronized (lock) { if (isExpired(tmpRoot, inputCache)) { // not initialized or inputCache newer than root, create new root tmpRoot = new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion()); root.get(appId).put(microserviceName, tmpRoot); return tmpRoot; } if (tmpRoot.isSameVersion(inputCache)) { // reuse root directly return tmpRoot; } } // root newer than inputCache, it's a minimal probability event: // 1) thread 1, use v1 inputCache, run into getOrCreateRoot, but did not run any code yet, suspend and switch to thread 2 // 2) thread 2, use v2 inputCache, v2 > v1, create new root // 3) thread 1 go on, then root is newer than inputCache // but if create old children in new version root, it's a wrong logic // so just create a temporary root for the inputCache, DO NOT assign to root tmpRoot = new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion()); root.get(appId).put(microserviceName, tmpRoot); return tmpRoot; } protected DiscoveryTreeNode doDiscovery(DiscoveryContext context, DiscoveryTreeNode parent) { for (int idx = 0; idx < filters.size(); ) { DiscoveryFilter filter = filters.get(idx); if (!filter.enabled()) { idx++; continue; } context.setCurrentNode(parent); DiscoveryTreeNode child = filter.discovery(context, parent); if (child == null) { // impossible, throw exception to help fix bug throw new ServiceCombException(filter.getClass().getName() + " discovery return null."); } child.level(idx + 1); if (!filter.isGroupingFilter()) { child.name(parent.name()); } if (child.isEmpty()) { // maybe need to rerun some filter DiscoveryTreeNode rerunNode = context.popRerunFilter(); if (rerunNode != null) { parent = rerunNode; idx = parent.level(); continue; } // no rerun support, go on even result is empty // because maybe some filter use other mechanism to create an instance(eg:domain name) } parent = child; idx++; } return parent; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.Map; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class DiscoveryTreeNode extends VersionedCache { private volatile boolean childrenInited; private volatile int level; protected Map attributes = new ConcurrentHashMapEx<>(); protected Map children = new ConcurrentHashMapEx<>(); public boolean childrenInited() { return childrenInited; } public DiscoveryTreeNode childrenInited(boolean childrenInited) { this.childrenInited = childrenInited; return this; } public int level() { return level; } public DiscoveryTreeNode level(int level) { this.level = level; return this; } @SuppressWarnings("unchecked") public T attribute(String key) { return (T) attributes.get(key); } public DiscoveryTreeNode attribute(String key, Object value) { attributes.put(key, value); return this; } public Map children() { return children; } public DiscoveryTreeNode children(Map children) { this.children = children; return this; } public DiscoveryTreeNode child(String childName) { return children.get(childName); } public DiscoveryTreeNode child(String childName, DiscoveryTreeNode child) { children.put(childName, child); return this; } public DiscoveryTreeNode fromCache(VersionedCache other) { this.cacheVersion = other.cacheVersion(); this.name = other.name(); this.data(other.data()); return this; } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstancePing.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import org.springframework.core.Ordered; public interface InstancePing extends Ordered { boolean ping(StatefulDiscoveryInstance instance); } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.PingStatus; public class InstanceStatusDiscoveryFilter extends AbstractGroupDiscoveryFilter { public static final String PARAMETER = "status_level"; public static final String GROUP_PREFIX = "status_group_"; public static final String GROUP_SIZE = "status_group_size"; public static final String SERVICECOMB_LOADBALANCE_FILTER_STATUS_ENABLED = "servicecomb.loadbalance.filter.status.enabled"; @Override public int getOrder() { return -10000; } @Override public boolean enabled() { if (enabled == null) { enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_STATUS_ENABLED, value -> enabled = value, true); } return enabled; } @Override protected String groupsSizeParameter() { return GROUP_SIZE; } @Override protected String contextParameter() { return PARAMETER; } @Override protected String groupPrefix() { return GROUP_PREFIX; } @Override public void init(DiscoveryContext context, DiscoveryTreeNode parent) { List instances = parent.data(); List level0 = new ArrayList<>(); List level1 = new ArrayList<>(); List level2 = new ArrayList<>(); List level3 = new ArrayList<>(); int groups = 1; for (StatefulDiscoveryInstance instance : instances) { if (HistoryStatus.CURRENT == instance.getHistoryStatus() && MicroserviceInstanceStatus.UP == instance.getStatus() && PingStatus.OK == instance.getPingStatus() && IsolationStatus.NORMAL == instance.getIsolationStatus()) { level0.add(instance); continue; } if (HistoryStatus.CURRENT == instance.getHistoryStatus() && MicroserviceInstanceStatus.UP == instance.getStatus() && PingStatus.UNKNOWN == instance.getPingStatus() && IsolationStatus.NORMAL == instance.getIsolationStatus()) { level1.add(instance); continue; } if (HistoryStatus.HISTORY == instance.getHistoryStatus() && MicroserviceInstanceStatus.UP == instance.getStatus() && PingStatus.OK == instance.getPingStatus() && IsolationStatus.NORMAL == instance.getIsolationStatus()) { level2.add(instance); continue; } level3.add(instance); } if (!level0.isEmpty()) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(level0)); groups++; } if (!level1.isEmpty()) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(level1)); groups++; } if (!level2.isEmpty()) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(level2)); groups++; } parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(level3)); parent.attribute(GROUP_SIZE, groups); } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/MicroserviceInstanceCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.util.concurrent.UncheckedExecutionException; /** * 在公钥认证场景,需要查询对端的实例信息。 * * 微服务实例缓存 key 为:serviceId@instanceId 缓存limit:1000 缓存老化策略:30分钟没有访问就过期。 * */ public class MicroserviceInstanceCache { private static final Logger logger = LoggerFactory.getLogger(MicroserviceInstanceCache.class); private static final Cache instances = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterAccess(30, TimeUnit.MINUTES) .build(); private final DiscoveryManager discoveryManager; public MicroserviceInstanceCache(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } public DiscoveryInstance getOrCreate(String application, String serviceName) { try { String key = String.format("%s@%s", application, serviceName); return instances.get(key, () -> { VersionedCache instances = discoveryManager.getOrCreateVersionedCache(application, serviceName); List statefulDiscoveryInstances = instances.data(); if (CollectionUtils.isEmpty(statefulDiscoveryInstances)) { throw new IllegalArgumentException("instance id not exists."); } return statefulDiscoveryInstances.get(0); }); } catch (ExecutionException | UncheckedExecutionException e) { logger.error("get microservice instance from cache failed, {}, {}", String.format("%s@%s", application, serviceName), e.getMessage()); return null; } } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; /** * Wrapper class for DiscoveryInstance with states like: isolation, health, metrics and so on. */ public class StatefulDiscoveryInstance extends AbstractDiscoveryInstance { public enum IsolationStatus { NORMAL, ISOLATED } public enum PingStatus { UNKNOWN, OK, FAIL } public enum HistoryStatus { CURRENT, HISTORY } private final DiscoveryInstance discoveryInstance; private IsolationStatus isolationStatus = IsolationStatus.NORMAL; private long isolatedTime; private long isolateDuration; private PingStatus pingStatus = PingStatus.UNKNOWN; private long pingTime = System.currentTimeMillis(); private HistoryStatus historyStatus = HistoryStatus.CURRENT; public StatefulDiscoveryInstance(DiscoveryInstance discoveryInstance) { this.discoveryInstance = discoveryInstance; } public IsolationStatus getIsolationStatus() { return isolationStatus; } public void setIsolationStatus( IsolationStatus isolationStatus) { this.isolationStatus = isolationStatus; } public PingStatus getPingStatus() { return pingStatus; } public void setPingStatus(PingStatus pingStatus) { this.pingStatus = pingStatus; } public HistoryStatus getHistoryStatus() { return historyStatus; } public void setHistoryStatus(HistoryStatus historyStatus) { this.historyStatus = historyStatus; } public long getIsolatedTime() { return isolatedTime; } public void setIsolatedTime(long isolatedTime) { this.isolatedTime = isolatedTime; } public long getIsolateDuration() { return isolateDuration; } public void setIsolateDuration(long isolateDuration) { this.isolateDuration = isolateDuration; } public long getPingTime() { return pingTime; } public void setPingTime(long pingTime) { this.pingTime = pingTime; } @Override public MicroserviceInstanceStatus getStatus() { return this.discoveryInstance.getStatus(); } @Override public String getRegistryName() { return this.discoveryInstance.getRegistryName(); } @Override public String getEnvironment() { return this.discoveryInstance.getEnvironment(); } @Override public String getApplication() { return this.discoveryInstance.getApplication(); } @Override public String getServiceName() { return this.discoveryInstance.getServiceName(); } @Override public String getAlias() { return this.discoveryInstance.getAlias(); } @Override public String getVersion() { return this.discoveryInstance.getVersion(); } @Override public DataCenterInfo getDataCenterInfo() { return this.discoveryInstance.getDataCenterInfo(); } @Override public String getDescription() { return this.discoveryInstance.getDescription(); } @Override public Map getProperties() { return this.discoveryInstance.getProperties(); } @Override public Map getSchemas() { return this.discoveryInstance.getSchemas(); } @Override public List getEndpoints() { return this.discoveryInstance.getEndpoints(); } @Override public String getInstanceId() { return this.discoveryInstance.getInstanceId(); } @Override public String getServiceId() { return this.discoveryInstance.getServiceId(); } } ================================================ FILE: foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/TelnetInstancePing.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.net.InetSocketAddress; import java.net.Socket; import org.apache.servicecomb.foundation.common.net.IpPort; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; public class TelnetInstancePing implements InstancePing { private static final Logger LOGGER = LoggerFactory.getLogger(TelnetInstancePing.class); @Override public boolean ping(StatefulDiscoveryInstance instance) { if (CollectionUtils.isEmpty(instance.getEndpoints())) { return false; } for (String endpoint : instance.getEndpoints()) { IpPort ipPort = NetUtils.parseIpPortFromURI(endpoint); try (Socket s = new Socket()) { s.connect(new InetSocketAddress(ipPort.getHostOrIp(), ipPort.getPort()), 3000); return true; } catch (Exception e) { LOGGER.warn("ping instance {}/{}/{}/{} endpoint {} failed", instance.getApplication(), instance.getServiceName(), instance.getRegistryName(), instance.getInstanceId(), endpoint); } } return false; } @Override public int getOrder() { return 10000; } } ================================================ FILE: foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.registry.consumer.SimpleMicroserviceInstancePing ================================================ FILE: foundations/foundation-registry/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.RegistryConfiguration ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestDiscoveryManager { @Test public void test_initial_service_list_correct() { MyDiscovery discovery1 = Mockito.spy(new MyDiscovery()); MyDiscoveryInstance instance1 = Mockito.mock(MyDiscoveryInstance.class); DiscoveryManager discoveryManager = new DiscoveryManager(List.of(discovery1), List.of(new TelnetInstancePing())); Mockito.when(discovery1.findServiceInstances("app", "service")) .thenReturn(List.of(instance1)); Mockito.when(instance1.getInstanceId()).thenReturn("instance1"); //first read VersionedCache versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); List result = versionedCache.data(); Assertions.assertEquals(1, result.size()); Assertions.assertEquals("instance1", result.get(0).getInstanceId()); Assertions.assertEquals(HistoryStatus.CURRENT, result.get(0).getHistoryStatus()); // second read versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); result = versionedCache.data(); Assertions.assertEquals(1, result.size()); Assertions.assertEquals("instance1", result.get(0).getInstanceId()); Assertions.assertEquals(HistoryStatus.CURRENT, result.get(0).getHistoryStatus()); } @Test public void test_initial_empty_service_list_correct() { MyDiscovery discovery1 = Mockito.spy(new MyDiscovery()); DiscoveryManager discoveryManager = new DiscoveryManager(List.of(discovery1), List.of(new TelnetInstancePing())); Mockito.when(discovery1.findServiceInstances("app", "service")) .thenReturn(Collections.emptyList()); //first read VersionedCache versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); List result = versionedCache.data(); Assertions.assertEquals(0, result.size()); // second read discoveryManager.getOrCreateVersionedCache("app", "service"); result = versionedCache.data(); Assertions.assertEquals(0, result.size()); } @Test public void test_isolate_service_instance_correct() { MyDiscovery discovery1 = Mockito.spy(new MyDiscovery()); MyDiscoveryInstance instance1 = Mockito.mock(MyDiscoveryInstance.class); DiscoveryManager discoveryManager = new DiscoveryManager(List.of(discovery1), List.of(new TelnetInstancePing())); Mockito.when(discovery1.findServiceInstances("app", "service")) .thenReturn(List.of(instance1)); Mockito.when(instance1.getInstanceId()).thenReturn("instance1"); Mockito.when(instance1.getApplication()).thenReturn("app"); Mockito.when(instance1.getServiceName()).thenReturn("service"); VersionedCache versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); List result = versionedCache.data(); discoveryManager.onInstanceIsolated(result.get(0), 10000L); versionedCache = discoveryManager.getOrCreateVersionedCache("app", "service"); result = versionedCache.data(); Assertions.assertEquals(1, result.size()); Assertions.assertEquals("instance1", result.get(0).getInstanceId()); Assertions.assertEquals(HistoryStatus.CURRENT, result.get(0).getHistoryStatus()); Assertions.assertEquals(IsolationStatus.ISOLATED, result.get(0).getIsolationStatus()); } static class MyDiscovery implements Discovery { @Override public String name() { return "my"; } @Override public boolean enabled(String application, String serviceName) { return true; } @Override public List findServiceInstances(String application, String serviceName) { return null; } @Override public List findServices(String application) { return null; } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { } @Override public void init() { } @Override public void run() { } @Override public void destroy() { } @Override public boolean enabled() { return true; } } static class MyDiscoveryInstance extends AbstractDiscoveryInstance { @Override public MicroserviceInstanceStatus getStatus() { return null; } @Override public String getRegistryName() { return null; } @Override public String getEnvironment() { return null; } @Override public String getApplication() { return null; } @Override public String getServiceName() { return null; } @Override public String getAlias() { return null; } @Override public String getVersion() { return null; } @Override public DataCenterInfo getDataCenterInfo() { return null; } @Override public String getDescription() { return null; } @Override public Map getProperties() { return null; } @Override public Map getSchemas() { return null; } @Override public List getEndpoints() { return null; } @Override public String getInstanceId() { return null; } } } ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractDiscoveryFilter { int initCallCount; int childrenInitedCallCount; boolean[] inited = new boolean[] {true, true}; DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode child = new DiscoveryTreeNode().name("c1"); DiscoveryTreeNode parent = new DiscoveryTreeNode() { public boolean childrenInited() { childrenInitedCallCount++; return inited[childrenInitedCallCount - 1]; } }; class AbstractDiscoveryFilterForTest extends AbstractDiscoveryFilter { @Override public int getOrder() { return 0; } @Override protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { initCallCount++; } @Override protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { return child.name(); } } AbstractDiscoveryFilterForTest filter = new AbstractDiscoveryFilterForTest(); DiscoveryTreeNode result; private void doDiscovery() { parent.child(child.name(), child); result = filter.discovery(context, parent); } @Test public void discoveryInited() { doDiscovery(); Assertions.assertEquals(1, childrenInitedCallCount); Assertions.assertEquals(0, initCallCount); Assertions.assertSame(child, result); } @Test public void discoveryNotInitedOnce() { inited[0] = false; doDiscovery(); Assertions.assertEquals(2, childrenInitedCallCount); Assertions.assertEquals(0, initCallCount); Assertions.assertSame(child, result); } @Test public void discoveryNotInitedTwice() { inited[0] = false; inited[1] = false; doDiscovery(); Assertions.assertEquals(2, childrenInitedCallCount); Assertions.assertEquals(1, initCallCount); Assertions.assertSame(child, result); } } ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractTransportDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestAbstractTransportDiscoveryFilter { class AbstractEndpointDiscoveryFilterForTest extends AbstractEndpointDiscoveryFilter { @Override public int getOrder() { return 0; } @Override protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent) { return transportName; } @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, StatefulDiscoveryInstance instance) { if (disableCreate) { return null; } return endpoint; } } boolean disableCreate; String transportName; AbstractEndpointDiscoveryFilterForTest filter = new AbstractEndpointDiscoveryFilterForTest(); DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent"); DiscoveryTreeNode result; @Test public void isGroupingFilter() { Assertions.assertTrue(filter.isGroupingFilter()); } @Test public void isTransportNameMatch_expectAll() { Assertions.assertTrue(filter.isTransportNameMatch("any", "")); } @Test public void isTransportNameMatch_equals() { Assertions.assertTrue(filter.isTransportNameMatch("rest", "rest")); } @Test public void isTransportNameMatch_notEquals() { Assertions.assertFalse(filter.isTransportNameMatch("rest", "highway")); } @Test public void discoveryNotExist() { transportName = "notExist"; parent.data(Collections.emptyList()); result = filter.discovery(context, parent); Assertions.assertTrue(result.isEmpty()); } @Test public void discoveryExist() { transportName = "rest"; DiscoveryTreeNode child = new DiscoveryTreeNode(); parent.child(transportName, child); result = filter.discovery(context, parent); Assertions.assertSame(child, result); } private StatefulDiscoveryInstance createInstance(String... schemas) { String id = UUID.randomUUID().toString(); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); List endpoints = new ArrayList<>(); for (int idx = 0; idx < schemas.length; idx++) { String schema = schemas[idx]; endpoints.add(String.format("%s://%s:%d", schema, id, 8080 + idx)); } Mockito.when(discoveryInstance.getInstanceId()).thenReturn(id); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(endpoints); return instance; } private List createMicroserviceInstances(StatefulDiscoveryInstance... instances) { List result = new ArrayList<>(); for (StatefulDiscoveryInstance instance : instances) { result.add(instance); } return result; } @Test public void createDiscoveryTree_oneTransport() { StatefulDiscoveryInstance instance1 = createInstance("a", "b"); StatefulDiscoveryInstance instance2 = createInstance("b"); List instances = createMicroserviceInstances(instance1, instance2); parent.data(instances); result = filter.createDiscoveryTreeNode("a", context, parent); Assertions.assertEquals("parent/a", result.name()); MatcherAssert.assertThat(result.collectionData(), Matchers.contains(instance1.getEndpoints().get(0))); } @Test public void createDiscoveryTree_allTransport() { StatefulDiscoveryInstance instance1 = createInstance("a", "b"); StatefulDiscoveryInstance instance2 = createInstance("b"); List instances = createMicroserviceInstances(instance1, instance2); parent.data(instances); result = filter.createDiscoveryTreeNode("", context, parent); Assertions.assertEquals("parent/", result.name()); List expect = new ArrayList<>(); expect.addAll(instance1.getEndpoints()); expect.addAll(instance2.getEndpoints()); MatcherAssert.assertThat(result.collectionData(), Matchers.contains(expect.toArray())); } @Test public void createDiscoveryTree_ignoreInvalid() { StatefulDiscoveryInstance instance1 = createInstance("a", "b"); StatefulDiscoveryInstance instance2 = createInstance(""); List instances = createMicroserviceInstances(instance1, instance2); parent.data(instances); result = filter.createDiscoveryTreeNode("", context, parent); Assertions.assertEquals("parent/", result.name()); MatcherAssert.assertThat(result.collectionData(), Matchers.contains(instance1.getEndpoints().toArray())); } @Test public void createEndpointNull() { disableCreate = true; StatefulDiscoveryInstance instance1 = createInstance("a", "b"); List instances = createMicroserviceInstances(instance1); parent.data(instances); result = filter.createDiscoveryTreeNode("", context, parent); Assertions.assertEquals("parent/", result.name()); MatcherAssert.assertThat(result.collectionData(), Matchers.empty()); } } ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDiscoveryContext { DiscoveryContext context = new DiscoveryContext(); @Test public void inputParameters() { Object inputParameters = new Object(); context.setInputParameters(inputParameters); Assertions.assertSame(inputParameters, context.getInputParameters()); } @Test public void contextParameters() { String name = "name"; Object value = new Object(); context.putContextParameter(name, value); Assertions.assertSame(value, context.getContextParameter(name)); Assertions.assertNull(context.getContextParameter("notExist")); } @Test public void rerun() { Assertions.assertNull(context.popRerunFilter()); DiscoveryTreeNode node = new DiscoveryTreeNode(); context.setCurrentNode(node); context.pushRerunFilter(); Assertions.assertSame(node, context.popRerunFilter()); Assertions.assertNull(context.popRerunFilter()); } } ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.registry.DiscoveryManager; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestDiscoveryTree { DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent"); DiscoveryTreeNode result; @BeforeEach public void before() { } @AfterEach public void tearDown() { } @Test public void isMatch_existingNull() { DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); Assertions.assertFalse(discoveryTree.isMatch(null, null)); } @Test public void isMatch_yes() { DiscoveryTree discoveryTree = new DiscoveryTree(new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(1); Assertions.assertTrue(discoveryTree.isMatch(new DiscoveryTreeNode().cacheVersion(1), parent)); } @Test public void isMatch_no() { DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(0); Assertions.assertFalse(discoveryTree.isExpired(new DiscoveryTreeNode().cacheVersion(1), parent)); } @Test public void isExpired_existingNull() { DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); Assertions.assertTrue(discoveryTree.isExpired(null, null)); } @Test public void isExpired_yes() { DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(1); Assertions.assertTrue(discoveryTree.isExpired(new DiscoveryTreeNode().cacheVersion(0), parent)); } @Test public void isExpired_no() { DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.cacheVersion(0); Assertions.assertFalse(discoveryTree.isExpired(new DiscoveryTreeNode().cacheVersion(0), parent)); } static class DiscoveryFilterForTest implements DiscoveryFilter { protected String groupName; public DiscoveryFilterForTest(String groupName) { this.groupName = groupName; } @Override public int getOrder() { return 0; } @Override public boolean isGroupingFilter() { return groupName != null; } @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { DiscoveryTreeNode child = new DiscoveryTreeNode(); if (groupName != null) { child.subName(parent, groupName); } return child; } } @Test public void filterNormal() { DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); parent.name("1.0.0-2.0.0"); discoveryTree.setDiscoveryFilters(Arrays.asList(new DiscoveryFilterForTest("g1"), new DiscoveryFilterForTest(null), new DiscoveryFilterForTest("g2"), new DiscoveryFilterForTest(null))); result = discoveryTree.discovery("app", "service", context, parent); Assertions.assertEquals("1.0.0-2.0.0/g1/g2", result.name()); } @Test public void easyDiscovery() { DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "svc")).thenReturn(parent); result = discoveryTree.discovery(context, "app", "svc"); Assertions.assertEquals(parent.name(), result.name()); Assertions.assertEquals(parent.cacheVersion(), result.cacheVersion()); } @Test public void discovery_filterReturnNull() { DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service")).thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); DiscoveryFilter filter = new DiscoveryFilter() { @Override public int getOrder() { return 0; } @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { return null; } }; discoveryTree.setDiscoveryFilters(Arrays.asList(filter)); ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, () -> result = discoveryTree.discovery(context, "app", "service")); Assertions.assertEquals(filter.getClass().getName() + " discovery return null.", exception.getMessage()); } @Test public void filterRerun() { parent.name("1.0.0-2.0.0"); DiscoveryFilterForTest f1 = new DiscoveryFilterForTest("g1") { @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { if (context.getContextParameter("step") == null) { context.pushRerunFilter(); context.putContextParameter("step", 1); return new DiscoveryTreeNode().name(groupName).data("first"); } return new DiscoveryTreeNode().name(groupName).data("second"); } }; DiscoveryFilterForTest f2 = new DiscoveryFilterForTest(null) { @Override public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { if ("first".equals(parent.data())) { return new DiscoveryTreeNode(); } return new DiscoveryTreeNode().data(parent.data()); } }; DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); discoveryTree.setDiscoveryFilters(Arrays.asList(f1, f2)); result = discoveryTree.discovery("app", "service", context, parent); Assertions.assertEquals("second", result.data()); } @Test @SuppressWarnings("unchecked") public void test_multi_service_discovery_correct() { DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); DiscoveryContext discoveryContext = new DiscoveryContext(); List service1 = Arrays.asList("s11", "s12"); Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service1")) .thenReturn(new VersionedCache().name("0+").data(service1)); DiscoveryTreeNode result = discoveryTree.discovery(discoveryContext, "app", "service1"); Assertions.assertArrayEquals(service1.toArray(new String[0]), ((List) result.data()).toArray(new String[0])); List service2 = Arrays.asList("s21", "s22"); Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service2")) .thenReturn(new VersionedCache().name("0+").data(service2)); result = discoveryTree.discovery(discoveryContext, "app", "service2"); Assertions.assertArrayEquals(service2.toArray(new String[0]), ((List) result.data()).toArray(new String[0])); result = discoveryTree.discovery(discoveryContext, "app", "service1"); Assertions.assertArrayEquals(service1.toArray(new String[0]), ((List) result.data()).toArray(new String[0])); } @Test @SuppressWarnings("unchecked") public void test_one_service_concurrent_correct() throws Exception { DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); DiscoveryContext discoveryContext = new DiscoveryContext(); InstanceStatusDiscoveryFilter filter = new InstanceStatusDiscoveryFilter(); DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.status.enabled"), Mockito.any(), Mockito.eq(true))) .thenReturn(true); filter.setDynamicProperties(dynamicProperties); discoveryTree.setDiscoveryFilters(List.of(filter)); StatefulDiscoveryInstance instance1 = Mockito.mock(StatefulDiscoveryInstance.class); StatefulDiscoveryInstance instance2 = Mockito.mock(StatefulDiscoveryInstance.class); VersionedCache expects0 = new VersionedCache().autoCacheVersion().name("0+") .data(Arrays.asList(instance1, instance2)); VersionedCache[] expects999 = new VersionedCache[999]; for (int i = 0; i < 999; i++) { expects999[i] = new VersionedCache().name("0+") .data(Arrays.asList(instance1, instance2)).cacheVersion(i + 1); } Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "service1")) .thenReturn(expects0, expects999); CountDownLatch countDownLatch = new CountDownLatch(1000); AtomicInteger success = new AtomicInteger(0); for (int i = 0; i < 10; i++) { new Thread(() -> { for (int j = 0; j < 100; j++) { DiscoveryTreeNode result = discoveryTree.discovery(discoveryContext, "app", "service1"); if (((List) result.data()).size() == 2) { success.getAndIncrement(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(3000, TimeUnit.MILLISECONDS); Assertions.assertEquals(1000, success.get()); } } ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTreeNode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDiscoveryTreeNode { DiscoveryTreeNode node = new DiscoveryTreeNode(); @Test public void childrenInited() { Assertions.assertFalse(node.childrenInited()); node.childrenInited(true); Assertions.assertTrue(node.childrenInited()); } @Test public void level() { node.level(1); Assertions.assertEquals(1, node.level()); } @Test public void attribute() { node.attribute("k1", "v1"); Assertions.assertEquals("v1", node.attribute("k1")); } @Test public void children() { Map children = new HashMap<>(); node.children(children); Assertions.assertSame(children, node.children()); } @Test public void child() { DiscoveryTreeNode child = new DiscoveryTreeNode().name("child"); node.child(child.name(), child); Assertions.assertSame(child, node.child(child.name())); } @Test public void fromCache() { Object data = new Object(); VersionedCache other = new VersionedCache().cacheVersion(1).name("cache").data(data); node.fromCache(other); Assertions.assertEquals(1, node.cacheVersion()); Assertions.assertEquals("cache", node.name()); Assertions.assertSame(data, node.data()); } } ================================================ FILE: foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.discovery; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.HistoryStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.IsolationStatus; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance.PingStatus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestInstanceStatusDiscoveryFilter { @Test public void test_all_group_correct_init() { InstanceStatusDiscoveryFilter filter = new InstanceStatusDiscoveryFilter(); DiscoveryTreeNode parent = new DiscoveryTreeNode(); List instances = new ArrayList<>(); StatefulDiscoveryInstance instance1 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance1.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); Mockito.when(instance1.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance1.getPingStatus()).thenReturn(PingStatus.OK); Mockito.when(instance1.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); StatefulDiscoveryInstance instance2 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance2.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); Mockito.when(instance2.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance2.getPingStatus()).thenReturn(PingStatus.UNKNOWN); Mockito.when(instance2.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); StatefulDiscoveryInstance instance3 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance3.getHistoryStatus()).thenReturn(HistoryStatus.HISTORY); Mockito.when(instance3.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance3.getPingStatus()).thenReturn(PingStatus.OK); Mockito.when(instance3.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); StatefulDiscoveryInstance instance4 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance4.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); Mockito.when(instance4.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance4.getPingStatus()).thenReturn(PingStatus.FAIL); Mockito.when(instance4.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); instances.addAll(Arrays.asList(instance1, instance2, instance3, instance4)); parent.name("parent"); parent.data(instances); filter.init(new DiscoveryContext(), parent); List level0 = parent .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 1) .data(); Assertions.assertEquals(1, level0.size()); List level1 = parent .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 2) .data(); Assertions.assertEquals(1, level1.size()); Assertions.assertEquals(1, level1.size()); List level2 = parent .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 3) .data(); Assertions.assertEquals(1, level2.size()); List level3 = parent .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 4) .data(); Assertions.assertEquals(1, level3.size()); Assertions.assertEquals(null, parent .child(InstanceStatusDiscoveryFilter.GROUP_PREFIX + 5)); } } ================================================ FILE: foundations/foundation-registry/src/test/resources/log4j2.xml ================================================ ================================================ FILE: foundations/foundation-spi/pom.xml ================================================ 4.0.0 org.apache.servicecomb foundations 3.4.0-SNAPSHOT foundation-spi Java Chassis::Foundations::SSL org.slf4j slf4j-api org.springframework spring-core org.jmockit jmockit test ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; public class AuthHeaderLoader { private final List authHeaderProviders = SPIServiceUtils.getSortedService(AuthHeaderProvider.class); private static final AuthHeaderLoader INSTANCE = new AuthHeaderLoader(); private AuthHeaderLoader() { } public static AuthHeaderLoader getInstance() { return INSTANCE; } public Map loadAuthHeaders(SignRequest signRequest) { Map result = new HashMap<>(); authHeaderProviders.forEach(provider -> result.putAll(provider.getSignAuthHeaders(signRequest))); return result; } } ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; import java.util.HashMap; import java.util.Map; public interface AuthHeaderProvider { /** * Obtain RBAC authentication request header, host is the key of cache * * @param host engine address ip * @return auth headers */ default Map authHeaders(String host) { return new HashMap<>(0); } default Map getSignAuthHeaders(SignRequest request) { String host = ""; if (request != null && request.getEndpoint() != null) { host = request.getEndpoint().getHost(); } return authHeaders(host); } } ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/SignRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.auth; import java.io.InputStream; import java.net.URI; import java.util.HashMap; import java.util.Map; public class SignRequest { /** * the resource path being requested */ private String resourcePath; /** * queryParams parameters being sent as part of this request. */ private Map queryParams; /** * Map of the headers included in this request */ private final Map headers = new HashMap<>(); /** * The service endpoint to which this request should be sent */ private URI endpoint; /** * The HTTP method to use when sending this request. */ private String httpMethod = "GET"; /** * An optional stream from which to read the request payload. */ private InputStream content; /** * The datetime in milliseconds for which the signature needs to be * computed. */ private final long signingDateTimeMilli = System.currentTimeMillis(); /** * The scope of the signature. */ private String scope; /** * The region to be used for computing the signature. */ private String regionName; /** * The name of the service. */ private String serviceName; /** * UTC formatted version of the signing time stamp. */ private String formattedSigningDateTime; /** * UTC Formatted Signing date with time stamp stripped */ private String formattedSigningDate; public SignRequest() { } public String getResourcePath() { return resourcePath; } public void setResourcePath(String resourcePath) { this.resourcePath = resourcePath; } public long getSigningDateTimeMilli() { return signingDateTimeMilli; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public String getRegionName() { if (regionName == null) { return ""; } return regionName; } public void setRegionName(String regionName) { this.regionName = regionName; } public String getServiceName() { if (serviceName == null) { return ""; } return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getFormattedSigningDateTime() { return formattedSigningDateTime; } public void setFormattedSigningDateTime(String formattedSigningDateTime) { this.formattedSigningDateTime = formattedSigningDateTime; } public String getFormattedSigningDate() { return formattedSigningDate; } public void setFormattedSigningDate(String formattedSigningDate) { this.formattedSigningDate = formattedSigningDate; } public Map getHeaders() { return headers; } public String getHttpMethod() { return httpMethod; } public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; } public void setEndpoint(URI endpoint) { this.endpoint = endpoint; } public URI getEndpoint() { return endpoint; } public InputStream getContent() { return content; } public void setContent(InputStream content) { this.content = content; } public void setHeaders(Map headers) { this.headers.clear(); this.headers.putAll(headers); } public Map getQueryParams() { return queryParams; } public void setQueryParams(Map queryParams) { this.queryParams = queryParams; } } ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/bootstrap/BootStrapService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.bootstrap; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import org.springframework.core.env.Environment; /** * A boot strap service is loaded after Spring Environment is created. * * In boot strap service, user's can only read configurations from Environment. Dynamic configurations * * from config center is not available. * * e.g. an authentication service must be connected before connecting to config center. * e.g. an config center configurations must be read before connecting to config center. */ public interface BootStrapService extends SPIOrder { void startup(Environment environment); } ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIEnabled.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public interface SPIEnabled { boolean enabled(); } ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIOrder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import org.springframework.core.Ordered; public interface SPIOrder extends Ordered { default int getOrder() { return 0; } } ================================================ FILE: foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.lang.reflect.Method; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; /** * SPI Service utils * * */ public final class SPIServiceUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SPIServiceUtils.class); // load one service, maybe trigger load another service // computeIfAbsent can not support this feature // so use double check private static final Object LOCK = new Object(); private static final Map, List> cache = new ConcurrentHashMap<>(); private SPIServiceUtils() { } /** * no cache, return new instances every time. */ public static List loadSortedService(Class serviceType) { List> serviceEntries = new ArrayList<>(); ServiceLoader serviceLoader = ServiceLoader.load(serviceType); serviceLoader.forEach(service -> { int serviceOrder = 0; Method getOrder = ReflectionUtils.findMethod(service.getClass(), "getOrder"); if (getOrder != null) { serviceOrder = (int) ReflectionUtils.invokeMethod(getOrder, service); } Entry entry = new SimpleEntry<>(serviceOrder, service); serviceEntries.add(entry); }); List services = serviceEntries.stream() .sorted(Comparator.comparingInt(Entry::getKey)) .map(Entry::getValue) .collect(Collectors.toList()); StringBuilder info = new StringBuilder(); for (int idx = 0; idx < services.size(); idx++) { T service = services.get(idx); info.append("{").append(idx).append(",").append(service.getClass().getSimpleName()).append("}"); } LOGGER.info("Found SPI service {}, services={}.", serviceType.getSimpleName(), info); return services; } @SuppressWarnings("unchecked") public static List getOrLoadSortedService(Class serviceType) { List services = cache.get(serviceType); if (services == null) { synchronized (LOCK) { services = cache.get(serviceType); if (services == null) { services = (List) loadSortedService(serviceType); cache.put(serviceType, services); } } } return (List) services; } /** * get target service.if target services are array,only random access to a service. */ public static T getTargetService(Class serviceType) { List services = getOrLoadSortedService(serviceType); if (services.isEmpty()) { LOGGER.info("Can not find SPI service for {}", serviceType.getName()); return null; } return services.get(0); } public static List getAllService(Class serviceType) { return getOrLoadSortedService(serviceType); } public static List getSortedService(Class serviceType) { return getOrLoadSortedService(serviceType); } public static T getPriorityHighestService(Class serviceType) { List services = getOrLoadSortedService(serviceType); if (services.isEmpty()) { LOGGER.info("Can not find SPI service for {}", serviceType.getName()); return null; } return services.get(0); } public static Collection getPriorityHighestServices(Function keyFunc, Class serviceType) { List services = getOrLoadSortedService(serviceType); if (services.isEmpty()) { LOGGER.info("Can not find SPI service for {}", serviceType.getName()); return null; } Map map = new HashMap<>(); services.forEach(instance -> map.putIfAbsent(keyFunc.apply(instance), instance)); return map.values(); } @SuppressWarnings("unchecked") public static IMPL getTargetService(Class serviceType, Class implType) { List services = getOrLoadSortedService(serviceType); return (IMPL) services .stream() .filter(service -> service.getClass().equals(implType)) .findFirst() .orElse(null); } } ================================================ FILE: foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public interface SPIServiceDef { } ================================================ FILE: foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public interface SPIServiceDef0 { } ================================================ FILE: foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public class SPIServiceDef0Impl implements SPIServiceDef0 { } ================================================ FILE: foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDefImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; public class SPIServiceDefImpl implements SPIServiceDef { } ================================================ FILE: foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.common.utils; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; import org.mockito.Mockito; import org.springframework.core.Ordered; import mockit.Deencapsulation; import mockit.Expectations; /** * Test SPIServiceUtils * * */ public class TestSPIServiceUtils { @Test public void testGetTargetServiceNull() { SPIServiceDef0 service = SPIServiceUtils.getTargetService(SPIServiceDef0.class); Assertions.assertNull(service); } @Test public void testGetTargetServiceNotNull() { SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class); Assertions.assertNotNull(service); Assertions.assertSame(service, SPIServiceUtils.getTargetService(SPIServiceDef.class)); } @Test public void testGetTargetService_special_null() { Assertions.assertNull(SPIServiceUtils.getTargetService(SPIServiceDef0.class, SPIServiceDef0Impl.class)); } @Test public void testGetTargetService_special_notNull() { SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class, SPIServiceDefImpl.class); Assertions.assertNotNull(service); } @Test @EnabledOnJre(JRE.JAVA_8) public void testSort() { Ordered o1 = Mockito.mock(Ordered.class); Ordered o2 = Mockito.mock(Ordered.class); Map map = new LinkedHashMap<>(); map.put("a", o1); map.put("b", o2); ServiceLoader serviceLoader = ServiceLoader.load(Ordered.class); Deencapsulation.setField(serviceLoader, "providers", map); new Expectations(ServiceLoader.class) { { o1.getOrder(); result = -1; o2.getOrder(); result = Integer.MAX_VALUE; ServiceLoader.load(Ordered.class); result = serviceLoader; } }; MatcherAssert.assertThat(SPIServiceUtils.getSortedService(Ordered.class), Matchers.contains(o1, o2)); MatcherAssert.assertThat(SPIServiceUtils.getAllService(Ordered.class), Matchers.contains(o1, o2)); MatcherAssert.assertThat(SPIServiceUtils.getPriorityHighestService(Ordered.class), Matchers.is(o1)); Map, List> cache = Deencapsulation.getField(SPIServiceUtils.class, "cache"); cache.clear(); } @Test public void getPriorityHighestService_null() { Assertions.assertNull(SPIServiceUtils.getPriorityHighestService(SPIServiceDef0.class)); } interface PriorityIntf { String getName(); int getOrder(); } public static class PriorityImpl implements PriorityIntf { private final String name; private final int order; public PriorityImpl(String name, int order) { this.name = name; this.order = order; } @Override public String getName() { return name; } @Override public int getOrder() { return order; } @Override public String toString() { return "PriorityImpl{" + "name='" + name + '\'' + ", order=" + order + '}'; } } @Test @EnabledOnJre(JRE.JAVA_8) public void getPriorityHighestServices() { Map instances = new LinkedHashMap<>(); instances.putIfAbsent("1", new PriorityImpl("n1", 0)); instances.putIfAbsent("2", new PriorityImpl("n1", -1)); instances.putIfAbsent("3", new PriorityImpl("n1", 1)); instances.putIfAbsent("4", new PriorityImpl("n2", 0)); instances.putIfAbsent("5", new PriorityImpl("n2", -1)); instances.putIfAbsent("6", new PriorityImpl("n2", 1)); ServiceLoader serviceLoader = ServiceLoader.load(PriorityIntf.class); Deencapsulation.setField(serviceLoader, "providers", instances); new Expectations(ServiceLoader.class) { { ServiceLoader.load(PriorityIntf.class); result = serviceLoader; } }; MatcherAssert.assertThat(SPIServiceUtils.getPriorityHighestServices(PriorityIntf::getName, PriorityIntf.class), Matchers.containsInAnyOrder(instances.get("2"), instances.get("5"))); Map, List> cache = Deencapsulation.getField(SPIServiceUtils.class, "cache"); cache.clear(); } } ================================================ FILE: foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.foundation.common.utils.SPIServiceDefImpl ================================================ FILE: foundations/foundation-ssl/pom.xml ================================================ 4.0.0 org.apache.servicecomb foundations 3.4.0-SNAPSHOT foundation-ssl Java Chassis::Foundations::SSL org.springframework spring-context org.apache.commons commons-lang3 org.slf4j slf4j-api io.netty netty-tcnative-boringssl-static org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-config test org.jmockit jmockit test ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/CertificateUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; /** * 证书处理的功能方法 * */ public final class CertificateUtil { private static final int SUBALTNAME_DNSNAME = 2; private static final int SUBALTNAME_IPADDRESS = 7; private CertificateUtil() { } /** * 将证书链进行排序。颁发机构的证书排在前面,所有者排在后面。如:rootCA > subCA > owner。 * 注意:传入的证书必须是“一条完整证书链"。 * @param cerChain * 将要排序的证书链。 * @return 排序后的证书链。 */ private static X509Certificate[] sort(X509Certificate[] cerChain) { X509Certificate[] chain = new X509Certificate[cerChain.length]; X509Certificate root = findRootCA(cerChain); chain[0] = root; for (int i = 1; i < chain.length; i++) { X509Certificate parent = chain[i - 1]; for (X509Certificate child : cerChain) { String parentDN = parent.getSubjectX500Principal().getName(); String childDN = child.getSubjectX500Principal().getName(); if (parentDN.equals(childDN)) { continue; } String childIssuerDN = child.getIssuerX500Principal().getName(); if (parentDN.equals(childIssuerDN)) { chain[i] = child; break; } } } return chain; } /** * 从证书链里面返回根证书,即自签名的证书。 * 注意:传入的证书必须是“一条完整证书链"。 * @param cerChain * 证书链。 * @return 根证书。 */ private static X509Certificate findRootCA(X509Certificate[] cerChain) { if (cerChain.length == 1) { return cerChain[0]; } for (X509Certificate item : cerChain) { String subjectDN = item.getSubjectX500Principal().getName(); String issuserDN = item.getIssuerX500Principal().getName(); if (subjectDN.equals(issuserDN)) { return item; } } throw new IllegalArgumentException("bad certificate chain: no root CA."); } /** * 从证书链里面返回证书所有者。即CA颁发的证书的所有者。位于证书链最下方。 * 注意:传入的证书必须是“一条完整证书链"。 * @param cerChain * 证书链。 * @return 所有者 */ public static X509Certificate findOwner(X509Certificate[] cerChain) { X509Certificate[] sorted = sort(cerChain); return sorted[sorted.length - 1]; } public static Set getCN(X509Certificate cert) { Set names = new HashSet<>(); // 读取CN String subjectDN = cert.getSubjectX500Principal().getName(); String[] pairs = subjectDN.split(","); for (String p : pairs) { String[] kv = p.split("="); if (kv.length == 2 && kv[0].equals("CN")) { names.add(kv[1]); } } // 读取SubjectAlternativeNames try { Collection> collection = cert.getSubjectAlternativeNames(); if (collection != null) { for (List list : collection) { if (list.size() == 2) { Object key = list.get(0); Object value = list.get(1); if (key instanceof Integer && value instanceof String) { int intKey = (Integer) key; String strValue = (String) value; if (intKey == SUBALTNAME_DNSNAME || intKey == SUBALTNAME_IPADDRESS) { names.add(strValue); } } } } } } catch (CertificateParsingException e) { throw new IllegalArgumentException("can not read AlternativeNames."); } return names; } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/ClientAuth.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; public final class ClientAuth { public static final String REQUIRED = "REQUIRED"; public static final String REQUEST = "REQUEST"; public static final String NONE = "NONE"; } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.KeyStore; import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Collection; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; public final class KeyStoreUtil { private KeyStoreUtil() { } public static KeyStore createKeyStore(String storeName, String storeType, char[] storeValue) { if (storeName == null) { return null; } File storeFile = new File(storeName); try { if (storeFile.isFile()) { return createKeyStore(new FileInputStream(storeFile), storeType, storeValue); } ClassLoader classLoader = Thread.currentThread().getContextClassLoader() == null ? KeyStoreUtil.class.getClassLoader() : Thread.currentThread().getContextClassLoader(); URL resource = classLoader.getResource(storeName); if (resource != null) { return createKeyStore(resource.openStream(), storeType, storeValue); } } catch (IOException e) { throw new IllegalArgumentException("Bad key store or value." + e.getMessage()); } return null; } public static KeyStore createKeyStore(InputStream store, String storeType, char[] storeValue) { try (InputStream is = store) { KeyStore keystore = KeyStore.getInstance(storeType); keystore.load(is, storeValue); return keystore; } catch (Exception e) { throw new IllegalArgumentException("Bad key store or value." + e.getMessage()); } } @SuppressWarnings({"rawtypes", "unchecked"}) public static CRL[] createCRL(String crlfile) { InputStream is = null; try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); is = new FileInputStream(crlfile); Collection c = cf.generateCRLs(is); return (CRL[]) c.toArray(new CRL[0]); } catch (CertificateException e) { throw new IllegalArgumentException("bad cert file."); } catch (FileNotFoundException e) { throw new IllegalArgumentException("crl file not found."); } catch (CRLException e) { throw new IllegalArgumentException("bad crl file."); } finally { if (is != null) { try { is.close(); } catch (IOException e) { ignore(); } } } } public static KeyManager[] createKeyManagers(final KeyStore keystore, char[] keyvalue) { try { KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmfactory.init(keystore, keyvalue); return kmfactory.getKeyManagers(); } catch (Exception e) { throw new IllegalArgumentException("Bad key store." + e.getMessage()); } } public static TrustManager[] createTrustManagers(final KeyStore keystore) { try { TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmfactory.init(keystore); return tmfactory.getTrustManagers(); } catch (Exception e) { throw new IllegalArgumentException("Bad trust store." + e.getMessage()); } } private static void ignore() { } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLCustom.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 和应用相关的信息,方便定制使用。 目前主要包含密码解密、证书路径、IP和Port等内容。 * */ public abstract class SSLCustom { private static final Logger LOG = LoggerFactory.getLogger(SSLCustom.class); public static SSLCustom defaultSSLCustom() { final SSLCustom custom = new SSLCustom() { @Override public char[] decode(char[] encrypted) { return encrypted; } @Override public String getFullPath(String filename) { return filename; } }; return custom; } public static SSLCustom createSSLCustom(String name) { try { if (name != null && !name.isEmpty()) { return (SSLCustom) Class.forName(name).getDeclaredConstructor().newInstance(); } } catch (ReflectiveOperationException e) { LOG.warn("init SSLCustom class failed, name is " + name); } return defaultSSLCustom(); } public abstract char[] decode(char[] encrypted); public abstract String getFullPath(String filename); public String getHost() { return null; } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.IOException; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; import org.apache.commons.lang3.StringUtils; /** * 根据传递的SSLOption构造SSL上下文。请参考JSSE获取相关API的层次参考。 * */ public final class SSLManager { private SSLManager() { } public static SSLContext createSSLContext(SSLOption option, SSLCustom custom) { try { String keyStoreName = custom.getFullPath(option.getKeyStore()); char[] keyStoreValue = option.getKeyStoreValue() == null ? new char[0] : custom.decode(option.getKeyStoreValue().toCharArray()); KeyStore keyStore = KeyStoreUtil.createKeyStore(keyStoreName, option.getKeyStoreType(), keyStoreValue); KeyManager[] keyManager = null; if (keyStore != null) { keyManager = KeyStoreUtil.createKeyManagers(keyStore, keyStoreValue); } String trustStoreName = custom.getFullPath(option.getTrustStore()); char[] trustStoreValue = option.getTrustStoreValue() == null ? new char[0] : custom.decode(option.getTrustStoreValue().toCharArray()); KeyStore trustStore = KeyStoreUtil.createKeyStore(trustStoreName, option.getTrustStoreType(), trustStoreValue); TrustManager[] trustManager; if (trustStore != null) { trustManager = KeyStoreUtil.createTrustManagers(trustStore); } else { trustManager = new TrustManager[] {new TrustAllManager()}; } TrustManager[] wrapped = new TrustManager[trustManager.length]; for (int i = 0; i < trustManager.length; i++) { wrapped[i] = new TrustManagerExt((X509ExtendedTrustManager) trustManager[i], option, custom); } // ?: ssl context version SSLContext context = SSLContext.getInstance("TLS"); context.init(keyManager, wrapped, new SecureRandom()); return context; } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException("NoSuchAlgorithmException." + e.getMessage()); } catch (KeyManagementException e) { throw new IllegalArgumentException("KeyManagementException." + e.getMessage()); } } public static SSLSocketFactory createSSLSocketFactory(SSLOption option, SSLCustom custom) { SSLContext context = createSSLContext(option, custom); SSLSocketFactory factory = context.getSocketFactory(); String[] supported = factory.getSupportedCipherSuites(); String[] enabled = option.getCiphers().split(","); return new SSLSocketFactoryExt(factory, getEnabledCiphers(supported, enabled), option.getProtocols().split(",")); } public static SSLEngine createSSLEngine(SSLOption option, SSLCustom custom) { SSLContext context = createSSLContext(option, custom); SSLEngine engine = context.createSSLEngine(); engine.setEnabledProtocols(option.getProtocols().split(",")); String[] supported = engine.getSupportedCipherSuites(); String[] enabled = option.getCiphers().split(","); engine.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); setClientAuth(option, engine); return engine; } public static SSLEngine createSSLEngine(SSLOption option, SSLCustom custom, String peerHost, int peerPort) { SSLContext context = createSSLContext(option, custom); SSLEngine engine = context.createSSLEngine(peerHost, peerPort); engine.setEnabledProtocols(option.getProtocols().split(",")); String[] supported = engine.getSupportedCipherSuites(); String[] enabled = option.getCiphers().split(","); engine.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); setClientAuth(option, engine); return engine; } private static void setClientAuth(SSLOption option, SSLEngine engine) { if (option.isAuthPeer() || ClientAuth.REQUIRED.equals(option.getClientAuth())) { engine.setNeedClientAuth(true); return; } if (ClientAuth.NONE.equals(option.getClientAuth())) { engine.setNeedClientAuth(false); engine.setWantClientAuth(false); return; } engine.setWantClientAuth(true); } private static void setClientAuth(SSLOption option, SSLServerSocket serverSocket) { if (option.isAuthPeer() || ClientAuth.REQUIRED.equals(option.getClientAuth())) { serverSocket.setNeedClientAuth(true); return; } if (ClientAuth.NONE.equals(option.getClientAuth())) { serverSocket.setNeedClientAuth(false); serverSocket.setWantClientAuth(false); return; } serverSocket.setWantClientAuth(true); } public static SSLServerSocket createSSLServerSocket(SSLOption option, SSLCustom custom) { try { SSLContext context = createSSLContext(option, custom); SSLServerSocketFactory factory = context.getServerSocketFactory(); SSLServerSocket socket = (SSLServerSocket) factory.createServerSocket(); socket.setEnabledProtocols(option.getProtocols().split(",")); String[] supported = socket.getSupportedCipherSuites(); String[] enabled = option.getCiphers().split(","); socket.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); setClientAuth(option, socket); return socket; } catch (UnknownHostException e) { throw new IllegalArgumentException("unknown host"); } catch (IOException e) { throw new IllegalArgumentException("unable create socket"); } } public static SSLSocket createSSLSocket(SSLOption option, SSLCustom custom) { try { SSLContext context = createSSLContext(option, custom); SSLSocketFactory factory = context.getSocketFactory(); SSLSocket socket = (SSLSocket) factory.createSocket(); socket.setEnabledProtocols(option.getProtocols().split(",")); String[] supported = socket.getSupportedCipherSuites(); String[] enabled = option.getCiphers().split("\\s*,\\s*"); socket.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); return socket; } catch (UnknownHostException e) { throw new IllegalArgumentException("unknown host"); } catch (IOException e) { throw new IllegalArgumentException("unable create socket"); } } private static String[] getEnabledCiphers(String[] supported, String[] enabled) { String[] result = new String[enabled.length]; int count = 0; for (String e : enabled) { for (String s : supported) { if (e.equals(s)) { result[count++] = e; break; } } } if (count == 0) { throw new IllegalArgumentException("no enabled cipher suits."); } String[] r = new String[count]; System.arraycopy(result, 0, r, 0, count); return r; } public static String[] getEnabledCiphers(SSLOption sslOption) { SSLOption option = new SSLOption(); if (StringUtils.isNotEmpty(sslOption.getProtocols())) { option.setProtocols(sslOption.getProtocols()); } else { option.setProtocols("TLSv1.2"); } option.setCiphers(sslOption.getCiphers()); SSLCustom custom = SSLCustom.defaultSSLCustom(); SSLSocket socket = createSSLSocket(option, custom); return socket.getEnabledCipherSuites(); } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.util.Properties; import org.springframework.core.env.Environment; /** * SSL配置选项。 * */ public final class SSLOption { public static final SSLOption DEFAULT_OPTION = new SSLOption(); public static final String DEFAULT_CIPHERS = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; static { DEFAULT_OPTION.setEngine("jdk"); DEFAULT_OPTION.setProtocols("TLSv1.2"); DEFAULT_OPTION.setCiphers(DEFAULT_CIPHERS); DEFAULT_OPTION.setAuthPeer(false); DEFAULT_OPTION.setCheckCNHost(false); DEFAULT_OPTION.setCheckCNWhite(false); DEFAULT_OPTION.setCheckCNWhiteFile("white.list"); DEFAULT_OPTION.setAllowRenegotiate(true); DEFAULT_OPTION.setStorePath("internal"); DEFAULT_OPTION.setTrustStore("trust.jks"); DEFAULT_OPTION.setTrustStoreType("JKS"); DEFAULT_OPTION.setTrustStoreValue("trustStoreValue"); DEFAULT_OPTION.setKeyStore("server.p12"); DEFAULT_OPTION.setKeyStoreType("PKCS12"); DEFAULT_OPTION.setKeyStoreValue("keyStoreValue"); DEFAULT_OPTION.setCrl("revoke.crl"); } private String engine; private String protocols; private String ciphers; private boolean authPeer; private boolean checkCNHost; private boolean checkCNWhite; private String checkCNWhiteFile; private boolean allowRenegotiate; private String clientAuth; private String storePath; private String trustStore; private String trustStoreType; private String trustStoreValue; private String keyStore; private String keyStoreType; private String keyStoreValue; private String crl; private String sslCustomClass; public String getEngine() { return engine; } public void setEngine(String engine) { this.engine = engine; } public void setProtocols(String protocols) { this.protocols = protocols; } public void setCiphers(String ciphers) { this.ciphers = ciphers; } public void setAuthPeer(boolean authPeer) { this.authPeer = authPeer; } public void setCheckCNHost(boolean checkCNHost) { this.checkCNHost = checkCNHost; } public void setCheckCNWhite(boolean checkCNWhite) { this.checkCNWhite = checkCNWhite; } public void setCheckCNWhiteFile(String checkCNWhiteFile) { this.checkCNWhiteFile = checkCNWhiteFile; } public void setAllowRenegotiate(boolean allowRenegotiate) { this.allowRenegotiate = allowRenegotiate; } public void setStorePath(String storePath) { this.storePath = storePath; } public void setTrustStore(String trustStore) { this.trustStore = trustStore; } public void setTrustStoreType(String trustStoreType) { this.trustStoreType = trustStoreType; } public void setTrustStoreValue(String trustStoreValue) { this.trustStoreValue = trustStoreValue; } public void setKeyStore(String keyStore) { this.keyStore = keyStore; } public void setKeyStoreType(String keyStoreType) { this.keyStoreType = keyStoreType; } public void setKeyStoreValue(String keyStoreValue) { this.keyStoreValue = keyStoreValue; } public void setCrl(String crl) { this.crl = crl; } public String getProtocols() { return protocols; } public String getCiphers() { return ciphers; } public boolean isAuthPeer() { return authPeer; } public boolean isCheckCNHost() { return checkCNHost; } public boolean isCheckCNWhite() { return checkCNWhite; } public String getCheckCNWhiteFile() { return checkCNWhiteFile; } public boolean isAllowRenegotiate() { return allowRenegotiate; } public String getStorePath() { return storePath; } public String getClientAuth() { return this.clientAuth; } public void setClientAuth(String clientAuth) { this.clientAuth = clientAuth; } public String getTrustStore() { return trustStore; } public String getTrustStoreType() { return trustStoreType; } public String getTrustStoreValue() { return trustStoreValue; } public String getKeyStore() { return keyStore; } public String getKeyStoreType() { return keyStoreType; } public String getKeyStoreValue() { return keyStoreValue; } public String getCrl() { return crl; } public static SSLOption build(String optionfile) { File file = new File(optionfile); if (!file.isFile()) { throw new IllegalArgumentException("Bad file name."); } try { SSLOption option = new SSLOption(); option.load(file.getCanonicalPath()); return option; } catch (IOException e) { throw new IllegalArgumentException("Bad file name."); } } public static SSLOption build(InputStream inputStream) { SSLOption option = new SSLOption(); option.load(inputStream); return option; } public static String getStringProperty(Environment environment, String defaultValue, String... keys) { String property = null; for (String key : keys) { property = environment.getProperty(key); if (property != null) { break; } } if (property != null) { return property; } else { return defaultValue; } } private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { String property = null; for (String key : keys) { property = environment.getProperty(key); if (property != null) { break; } } if (property != null) { return Boolean.parseBoolean(property); } else { return defaultValue; } } public static SSLOption build(String tag, Environment environment) { SSLOption option = new SSLOption(); option.engine = getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine"); option.protocols = getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols"); option.ciphers = getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers"); option.authPeer = getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer"); option.checkCNHost = getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host"); option.checkCNWhite = getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white"); option.checkCNWhiteFile = getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file"); option.allowRenegotiate = getBooleanProperty(environment, DEFAULT_OPTION.isAllowRenegotiate(), "ssl." + tag + ".allowRenegotiate", "ssl.allowRenegotiate"); option.storePath = getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath"); option.clientAuth = getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".storePath", "ssl.clientAuth"); option.trustStore = getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore"); option.trustStoreType = getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType"); option.trustStoreValue = getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue"); option.keyStore = getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore"); option.keyStoreType = getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType"); option.keyStoreValue = getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue"); option.crl = getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl"); option.sslCustomClass = getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass"); return option; } private void fromProperty(Properties props) { this.protocols = propString(props, "ssl.protocols"); this.ciphers = propString(props, "ssl.ciphers"); this.authPeer = propBoolean(props, "ssl.authPeer"); this.checkCNHost = propBoolean(props, "ssl.checkCN.host"); this.checkCNWhite = propBoolean(props, "ssl.checkCN.white"); this.checkCNWhiteFile = propString(props, "ssl.checkCN.white.file"); this.allowRenegotiate = propBoolean(props, "ssl.allowRenegotiate"); this.storePath = propString(props, "ssl.storePath"); this.clientAuth = propString(props, "ssl.clientAuth", false); this.trustStore = propString(props, "ssl.trustStore"); this.trustStoreType = propString(props, "ssl.trustStoreType"); this.trustStoreValue = propString(props, "ssl.trustStoreValue"); this.keyStore = propString(props, "ssl.keyStore"); this.keyStoreType = propString(props, "ssl.keyStoreType"); this.keyStoreValue = propString(props, "ssl.keyStoreValue"); this.crl = propString(props, "ssl.crl"); this.sslCustomClass = props.getProperty("ssl.sslCustomClass"); } private String propString(Properties props, String key) { return propString(props, key, true); } private String propString(Properties props, String key, boolean required) { String s = props.getProperty(key); if (s == null && required) { throw new IllegalArgumentException("No key :" + key); } return s; } private boolean propBoolean(Properties props, String key) { String s = props.getProperty(key); if (s == null) { throw new IllegalArgumentException("No key :" + key); } return Boolean.parseBoolean(s); } private void load(InputStream inputStream) { Properties props = new Properties(); Reader reader = null; try { reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); props.load(reader); fromProperty(props); } catch (IOException e) { throw new IllegalArgumentException( "Can not read ssl client config file"); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { ignore(); } } if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { ignore(); } } } } // visible for testing void load(String path) { try { load(new FileInputStream(path)); } catch (FileNotFoundException e) { throw new IllegalArgumentException( "Can not read ssl client config file: " + path); } } private void ignore() { } public String getSslCustomClass() { return this.sslCustomClass; } public void setSslCustomClass(String sslCustomClass) { this.sslCustomClass = sslCustomClass; } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOptionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import org.springframework.core.env.Environment; public interface SSLOptionFactory { static SSLOptionFactory createSSLOptionFactory(String tag, Environment environment) { String name = SSLOption.getStringProperty(environment, null, "ssl." + tag + ".sslOptionFactory", "ssl.sslOptionFactory"); return createSSLOptionFactory(name); } static SSLOptionFactory createSSLOptionFactory(String className) { if (className != null && !className.isEmpty()) { try { return (SSLOptionFactory) Class.forName(className).getDeclaredConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new IllegalStateException("Failed to create SSLOptionFactory.", e); } } return null; } SSLOption createSSLOption(); } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLSocketFactoryExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; /** * 扩展SSLSocketFactory,设置算法和协议列表。 * */ public class SSLSocketFactoryExt extends SSLSocketFactory { private final SSLSocketFactory sslSocketFactory; private final String[] ciphers; private final String[] protos; public SSLSocketFactoryExt(SSLSocketFactory factory, String[] ciphers, String[] protos) { this.sslSocketFactory = factory; this.ciphers = ciphers; this.protos = protos; } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(s, host, port, autoClose)); } @Override public String[] getDefaultCipherSuites() { return this.sslSocketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return this.sslSocketFactory.getSupportedCipherSuites(); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(address, port, localAddress, localPort)); } private SSLSocket wrapSocket(SSLSocket socket) { socket.setEnabledProtocols(protos); socket.setEnabledCipherSuites(ciphers); return socket; } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustAllManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.net.Socket; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLEngine; import javax.net.ssl.X509ExtendedTrustManager; /** it is not secure */ public class TrustAllManager extends X509ExtendedTrustManager { @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { } } ================================================ FILE: foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.nio.charset.StandardCharsets; import java.security.cert.CRL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Enumeration; import java.util.Set; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.X509ExtendedTrustManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 扩展TurstManager * */ public class TrustManagerExt extends X509ExtendedTrustManager { private static final Logger LOG = LoggerFactory.getLogger(TrustManagerExt.class); private static final int WHITE_SIZE = 1024; private final X509ExtendedTrustManager trustManager; private final SSLOption option; private final SSLCustom custom; public TrustManagerExt(X509ExtendedTrustManager manager, SSLOption option, SSLCustom custom) { this.trustManager = manager; this.option = option; this.custom = custom; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!option.isAuthPeer()) { return; } checkTrustedCustom(chain, null); trustManager.checkClientTrusted(chain, authType); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!option.isAuthPeer()) { return; } checkTrustedCustom(chain, null); trustManager.checkServerTrusted(chain, authType); } @Override public X509Certificate[] getAcceptedIssuers() { return trustManager.getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { if (!option.isAuthPeer()) { return; } String ip = null; if (socket != null && socket.isConnected() && socket instanceof SSLSocket) { InetAddress inetAddress = socket.getInetAddress(); if (inetAddress != null) { ip = inetAddress.getHostAddress(); } } checkTrustedCustom(chain, ip); trustManager.checkClientTrusted(chain, authType, socket); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { if (!option.isAuthPeer()) { return; } String ip = null; if (engine != null) { SSLSession session = engine.getHandshakeSession(); ip = session.getPeerHost(); } checkTrustedCustom(chain, ip); trustManager.checkClientTrusted(chain, authType, engine); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { if (!option.isAuthPeer()) { return; } String ip = null; if (socket != null && socket.isConnected() && socket instanceof SSLSocket) { InetAddress inetAddress = socket.getInetAddress(); if (inetAddress != null) { ip = inetAddress.getHostAddress(); } } checkTrustedCustom(chain, ip); trustManager.checkServerTrusted(chain, authType, socket); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { if (!option.isAuthPeer()) { return; } String ip = null; if (engine != null) { SSLSession session = engine.getHandshakeSession(); ip = session.getPeerHost(); } checkTrustedCustom(chain, ip); trustManager.checkServerTrusted(chain, authType, engine); } private void checkTrustedCustom(X509Certificate[] chain, String ip) throws CertificateException { checkCNHost(chain, ip); checkCNWhite(chain); checkCRL(chain); } // ? : learn java default / apache CN check private void checkCNHost(X509Certificate[] chain, String ip) throws CertificateException { if (option.isCheckCNHost()) { X509Certificate owner = CertificateUtil.findOwner(chain); Set cns = CertificateUtil.getCN(owner); String ipTmp = ip == null ? custom.getHost() : ip; // 从本机来的请求, 只要CN与本机的任何一个IP地址匹配即可 if ("127.0.0.1".equals(ipTmp)) { try { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); if (interfaces != null) { while (interfaces.hasMoreElements()) { NetworkInterface nif = interfaces.nextElement(); Enumeration ias = nif.getInetAddresses(); while (ias.hasMoreElements()) { InetAddress ia = ias.nextElement(); String local = ia.getHostAddress(); if (cnValid(cns, local)) { return; } } } } } catch (SocketException e) { throw new CertificateException("Get local address fail."); } } else if (cnValid(cns, ipTmp)) { return; } LOG.error("CN does not match IP: e=" + cns + ",t=" + ip); throw new CertificateException("CN does not match IP: e=" + cns + ",t=" + ip); } } private boolean cnValid(Set certsCN, String srcCN) { for (String cert : certsCN) { if (cert.equals(srcCN)) { return true; } } return false; } private void checkCNWhite(X509Certificate[] chain) throws CertificateException { if (option.isCheckCNWhite()) { FileInputStream fis = null; InputStreamReader reader = null; try { String white = option.getCheckCNWhiteFile(); white = custom.getFullPath(white); fis = new FileInputStream(white); reader = new InputStreamReader(fis, StandardCharsets.UTF_8); char[] buffer = new char[WHITE_SIZE]; int len = reader.read(buffer); String[] cns = new String(buffer, 0, len).split("\\s+"); X509Certificate owner = CertificateUtil.findOwner(chain); Set certCN = CertificateUtil.getCN(owner); for (String c : cns) { if (cnValid(certCN, c)) { return; } } } catch (FileNotFoundException e) { throw new CertificateException( "CN does not match white. no white file."); } catch (IOException e) { throw new CertificateException( "CN does not match white. can not read file."); } finally { try { if (reader != null) { reader.close(); } } catch (IOException e) { ignore(); } try { if (fis != null) { fis.close(); } } catch (IOException e) { ignore(); } } LOG.error("CN does not match white."); throw new CertificateException("CN does not match white."); } } private void checkCRL(X509Certificate[] chain) throws CertificateException { String crl = option.getCrl(); crl = custom.getFullPath(crl); File file = new File(crl); if (!file.exists()) { return; } CRL[] crls = KeyStoreUtil.createCRL(crl); X509Certificate owner = CertificateUtil.findOwner(chain); for (CRL c : crls) { if (c.isRevoked(owner)) { LOG.error("certificate revoked"); throw new CertificateException("certificate revoked"); } } } private static void ignore() { } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/CertificateUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Set; import javax.security.auth.x500.X500Principal; import org.junit.After; import org.junit.Before; import org.junit.Test; import mockit.Expectations; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class CertificateUtilTest { @SuppressWarnings("deprecation") static class MyX509Certificate extends X509Certificate { private static final long serialVersionUID = -3585440601605666278L; public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { } @Override public boolean hasUnsupportedCriticalExtension() { return false; } @Override public Set getCriticalExtensionOIDs() { return null; } @Override public Set getNonCriticalExtensionOIDs() { return null; } @Override public byte[] getExtensionValue(String oid) { return null; } @Override public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { } @Override public int getVersion() { return 0; } @Override public BigInteger getSerialNumber() { return null; } @Override public Principal getIssuerDN() { return null; } @Override public Principal getSubjectDN() { return null; } @Override public Date getNotBefore() { return null; } @Override public Date getNotAfter() { return null; } @Override public byte[] getTBSCertificate() throws CertificateEncodingException { return null; } @Override public byte[] getSignature() { return null; } @Override public String getSigAlgName() { return null; } @Override public String getSigAlgOID() { return null; } @Override public byte[] getSigAlgParams() { return null; } @Override public boolean[] getIssuerUniqueID() { return null; } @Override public boolean[] getSubjectUniqueID() { return null; } @Override public boolean[] getKeyUsage() { return null; } @Override public int getBasicConstraints() { return 0; } @Override public byte[] getEncoded() throws CertificateEncodingException { return null; } @Override public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } @Override public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } @Override public String toString() { return null; } @Override public PublicKey getPublicKey() { return null; } } @Before public void setUp() { } @After public void tearDown() { } @Test public void testGetCN(@Mocked X500Principal aX500Principal, @Mocked MyX509Certificate myX509Certificate) { new Expectations() { { aX500Principal.getName(); result = "CN=Test1234"; myX509Certificate.getSubjectX500Principal(); result = aX500Principal; } }; MyX509Certificate xxmyX509Certificate = new MyX509Certificate(); Set strExpect = CertificateUtil.getCN(xxmyX509Certificate); Assertions.assertTrue(strExpect.contains("Test1234")); } @Test public void testGetCNException(@Mocked X500Principal aX500Principal, @Mocked MyX509Certificate myX509Certificate) { new Expectations() { { aX500Principal.getName(); result = "NOCN=Test1234"; myX509Certificate.getSubjectX500Principal(); result = aX500Principal; } }; MyX509Certificate xxmyX509Certificate = new MyX509Certificate(); try { Set strExpect = CertificateUtil.getCN(xxmyX509Certificate); Assertions.assertEquals(strExpect.size(), 0); } catch (IllegalArgumentException e) { Assertions.assertNotNull(null); } } @Test public void testFindOwner(@Mocked X500Principal aX500Principal1, @Mocked X500Principal aX500Principal2, @Mocked MyX509Certificate myX509Certificate) { new Expectations() { { aX500Principal1.getName(); result = "Huawei"; } { aX500Principal2.getName(); result = "Huawei"; } { myX509Certificate.getSubjectX500Principal(); result = aX500Principal1; myX509Certificate.getIssuerX500Principal(); result = aX500Principal2; } }; MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); MyX509Certificate[] xxmyX509Certificate = new MyX509Certificate[2]; xxmyX509Certificate[0] = myX509Certificate1; xxmyX509Certificate[1] = myX509Certificate2; X509Certificate aX509Certificate = CertificateUtil.findOwner(xxmyX509Certificate); Assertions.assertNull(aX509Certificate); } @Test public void testFindRootCAException(@Mocked X500Principal aX500Principal1, @Mocked X500Principal aX500Principal2, @Mocked MyX509Certificate myX509Certificate) { new Expectations() { { aX500Principal1.getName(); result = "Huawei1"; } { aX500Principal2.getName(); result = "Huawei3"; } { myX509Certificate.getSubjectX500Principal(); result = aX500Principal1; myX509Certificate.getIssuerX500Principal(); result = aX500Principal2; } }; MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); MyX509Certificate[] xxmyX509Certificate = new MyX509Certificate[2]; xxmyX509Certificate[0] = myX509Certificate1; xxmyX509Certificate[1] = myX509Certificate2; try { X509Certificate aX509Certificate = CertificateUtil.findOwner(xxmyX509Certificate); Assertions.assertNull(aX509Certificate); } catch (IllegalArgumentException e) { Assertions.assertEquals("bad certificate chain: no root CA.", e.getMessage()); } } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.FileNotFoundException; import java.io.InputStream; import java.security.KeyStore; import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Collection; import org.junit.Test; import org.junit.jupiter.api.Assertions; import mockit.Mock; import mockit.MockUp; import org.mockito.Mockito; public class KeyStoreUtilTest { final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); @Test public void testCreateKeyStoreException() { String storename = ""; String storetype = "testType"; char[] storevalue = "Changeme_123".toCharArray(); try { KeyStoreUtil.createKeyStore(storename, storetype, storevalue); } catch (IllegalArgumentException e) { Assertions.assertEquals("Bad key store or value.testType not found", e.getMessage()); } } @Test public void testCreateKeyStoreException2() { String storename = strFilePath + "/ssl/trust.jks"; String storetype = "PKCS12"; char[] storevalue = "Changeme_123".toCharArray(); try { KeyStoreUtil.createKeyStore(storename, storetype, storevalue); } catch (IllegalArgumentException e) { Assertions.assertEquals("Bad key store or value.DerInputStream.getLength(): lengthTag=109, too big.", e.getMessage()); } } @Test public void testCreateKeyManagersException() { KeyStore keystore; String storename = strFilePath + "/ssl/server.p12"; String storetype = "PKCS12"; char[] storevalue = "Changeme_123".toCharArray(); keystore = KeyStoreUtil.createKeyStore(storename, storetype, storevalue); char[] storeKeyValue = null; try { KeyStoreUtil.createKeyManagers(keystore, storeKeyValue); } catch (IllegalArgumentException e) { Assertions.assertEquals("Bad key store.Get Key failed:" + " Cannot read the array length because \"password\" is null", e.getMessage()); } } @Test public void testCreateCRL() { String crlfile = strFilePath + "/ssl/server.p12"; mockGenerateCRLs(); boolean validAssert = true; try { CRL[] crl = KeyStoreUtil.createCRL(crlfile); Assertions.assertNull(crl); } catch (Exception e) { validAssert = false; } Assertions.assertTrue(validAssert); } @Test public void testCreateCRLException() { String crlfile = strFilePath + "/ssl/server.p12"; boolean validAssert = true; try { new MockUp() { @Mock public CertificateFactory getInstance(String type) throws CertificateException { throw new CertificateException(); } }; KeyStoreUtil.createCRL(crlfile); } catch (Exception e) { validAssert = false; } Assertions.assertFalse(validAssert); } @Test public void testExceptionFileNotFound() { String crlfile = strFilePath + "/ssl/server.p12"; boolean validAssert = true; try { new MockUp() { @Mock public CertificateFactory getInstance( String type) throws CertificateException, FileNotFoundException { throw new FileNotFoundException(); } }; KeyStoreUtil.createCRL(crlfile); } catch (Exception e) { validAssert = false; Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } Assertions.assertFalse(validAssert); } @Test public void testExceptionCRLException() { String crlfile = strFilePath + "/ssl/server.p12"; boolean validAssert = true; try { new MockUp() { @Mock public CertificateFactory getInstance(String type) throws CertificateException, CRLException { throw new CRLException(); } }; KeyStoreUtil.createCRL(crlfile); } catch (Exception e) { validAssert = false; Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } Assertions.assertFalse(validAssert); } private void mockGenerateCRLs() { new MockUp() { @SuppressWarnings("unchecked") @Mock public Collection generateCRLs(InputStream inStream) throws CRLException { return Mockito.mock(Collection.class); } }; } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/MyOptionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; public class MyOptionFactory implements SSLOptionFactory { @Override public SSLOption createSSLOption() { SSLOption option = new SSLOption(); option.setProtocols("TLSv1.2"); option.setCiphers("TLS_RSA_WITH_AES_128_CBC_SHA256"); option.setAuthPeer(false); option.setCheckCNHost(false); option.setCheckCNWhite(false); option.setCheckCNWhiteFile("white.list"); return option; } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.IOException; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Enumeration; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import org.junit.Test; import org.junit.jupiter.api.Assertions; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class SSLManagerTest { private final String DIR = Thread.currentThread().getContextClassLoader().getResource("").getPath(); @Test public void testSSLManagerServerAndClient(final @Mocked NetworkInterface nif) throws Exception { final InetAddress ia = Inet4Address.getByName("10.57.65.225"); final Enumeration interfaces = new Enumeration() { final int count = 1; int cur = 0; @Override public boolean hasMoreElements() { if (cur < count) { cur++; return true; } return false; } @Override public NetworkInterface nextElement() { return nif; } }; final Enumeration ias = new Enumeration() { final int count = 1; int cur = 0; @Override public boolean hasMoreElements() { if (cur < count) { cur++; return true; } return false; } @Override public InetAddress nextElement() { return ia; } }; new Expectations() { @Mocked NetworkInterface nif; { NetworkInterface.getNetworkInterfaces(); result = interfaces; } }; new Expectations() { { nif.getInetAddresses(); result = ias; ia.getHostAddress(); result = "10.57.65.225"; } }; SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; final SSLServerSocket serverSocket = SSLManager.createSSLServerSocket(option, custom); Assertions.assertTrue(serverSocket.getNeedClientAuth()); serverSocket.bind(new InetSocketAddress("127.0.0.1", 8886)); String[] protos = serverSocket.getEnabledCipherSuites(); String[] protosExpected = "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA" .split(","); Assertions.assertArrayEquals(protos, protosExpected); String[] ciphers = serverSocket.getEnabledCipherSuites(); String[] ciphersExpected = "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA" .split(","); Assertions.assertArrayEquals(ciphers, ciphersExpected); Assertions.assertTrue(serverSocket.getNeedClientAuth()); SSLOption clientoption = SSLOption.build(DIR + "/client.ssl.properties"); SSLSocket clientsocket = SSLManager.createSSLSocket(clientoption, custom); String[] clientprotos = clientsocket.getEnabledCipherSuites(); String[] clientprotosExpected = "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA" .split(","); Assertions.assertArrayEquals(clientprotos, clientprotosExpected); String[] clientciphers = clientsocket.getEnabledCipherSuites(); String[] clientciphersExpected = "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA" .split(","); Assertions.assertArrayEquals(clientciphers, clientciphersExpected); Assertions.assertFalse(clientsocket.getNeedClientAuth()); boolean validAssert = true; try { clientsocket.connect(new InetSocketAddress("127.0.0.1", 8886)); new Thread(() -> { try { SSLSocket s = (SSLSocket) serverSocket.accept(); s.addHandshakeCompletedListener(arg0 -> { }); s.getOutputStream().write(new byte[] {0, 1}); } catch (IOException e) { e.printStackTrace(); Assertions.fail("this should not happen"); } }).start(); clientsocket.startHandshake(); clientsocket.close(); serverSocket.close(); // socked successfully opened and closed } catch (Exception e) { e.printStackTrace(); validAssert = false; } Assertions.assertTrue(validAssert); } @Test public void testCreateSSLEngine() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom); // if client mode may not decided at initialization. Different JDK is different, do not check it. // Assertions.assertEquals(false, aSSLEngine.getUseClientMode()); Assertions.assertTrue(aSSLEngine.getNeedClientAuth()); } @Test public void testCreateSSLEngineClientAuthNone() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); option.setClientAuth("NONE"); option.setAuthPeer(false); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom); // if client mode may not decided at initialization. Different JDK is different, do not check it. // Assertions.assertEquals(false, aSSLEngine.getUseClientMode()); Assertions.assertFalse(aSSLEngine.getNeedClientAuth()); Assertions.assertFalse(aSSLEngine.getWantClientAuth()); } @Test public void testCreateSSLEnginewithPort() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; int port = 39093; String peerHost = "host1"; SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom, peerHost, port); Assertions.assertNotNull(aSSLEngine); Assertions.assertEquals("host1", aSSLEngine.getPeerHost()); } @Test public void testCreateSSLContextResource() { SSLOption option = SSLOption.build(DIR + "/server.ssl.resource.properties"); SSLCustom custom = SSLCustom.defaultSSLCustom(); SSLContext context = SSLManager.createSSLContext(option, custom); Assertions.assertNotNull(context); } @Test public void testCreateSSLContextException() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; new MockUp() { @Mock public SSLContext getInstance(String type) throws NoSuchAlgorithmException { throw new NoSuchAlgorithmException(); } }; try { SSLManager.createSSLContext(option, custom); Assertions.assertNotNull(null); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testCreateSSLContextKeyManagementException() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; new MockUp() { @Mock public SSLContext getInstance(String type) throws KeyManagementException { throw new KeyManagementException(); } }; try { SSLManager.createSSLContext(option, custom); Assertions.assertNotNull(null); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testCreateSSLServerSocketException() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; new MockUp() { @Mock public SSLContext getInstance(String type) throws UnknownHostException { throw new UnknownHostException(); } }; try { SSLManager.createSSLServerSocket(option, custom); Assertions.assertNotNull(null); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testCreateSSLServerSocketIOException() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; new MockUp() { @Mock public SSLContext getInstance(String type) throws IOException { throw new IOException(); } }; try { SSLManager.createSSLServerSocket(option, custom); Assertions.assertNotNull(null); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testCreateSSLSocketException() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; new MockUp() { @Mock public SSLContext getInstance(String type) throws UnknownHostException { throw new UnknownHostException(); } }; try { SSLManager.createSSLSocket(option, custom); Assertions.assertNotNull(null); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testCreateSSLSocketIOException() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; new MockUp() { @Mock public SSLContext getInstance(String type) throws IOException { throw new IOException(); } }; try { SSLManager.createSSLSocket(option, custom); Assertions.assertNotNull(null); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testCreateSSLSocketFactory() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return DIR + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } }; SSLSocketFactory aSSLSocketFactory = SSLManager.createSSLSocketFactory(option, custom); Assertions.assertNotNull(aSSLSocketFactory.getDefaultCipherSuites()[0]); } @Test public void testGetSupportedCiphers() { SSLOption option = new SSLOption(); option.setCiphers("TLS_RSA_WITH_AES_128_GCM_SHA256"); option.setProtocols("TLSv1.2"); String[] ciphers = SSLManager.getEnabledCiphers(option); Assertions.assertEquals(ciphers[0], "TLS_RSA_WITH_AES_128_GCM_SHA256"); } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.net.URL; import java.util.Properties; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import mockit.Mock; import mockit.MockUp; public class SSLOptionTest { private static final String DIR = Thread.currentThread().getContextClassLoader().getResource("").getPath(); Environment environment = Mockito.mock(Environment.class); @Before public void setUp() throws Exception { Mockito.when(environment.getProperty("ssl.protocols")).thenReturn("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello"); Mockito.when(environment.getProperty("ssl.ciphers")).thenReturn( "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"); Mockito.when(environment.getProperty("ssl.authPeer")).thenReturn("true"); Mockito.when(environment.getProperty("ssl.checkCN.host")).thenReturn("true"); Mockito.when(environment.getProperty("ssl.checkCN.white")).thenReturn("true"); Mockito.when(environment.getProperty("ssl.checkCN.white.file")).thenReturn("white.list"); Mockito.when(environment.getProperty("ssl.allowRenegotiate")).thenReturn("false"); Mockito.when(environment.getProperty("ssl.storePath")).thenReturn("internal"); Mockito.when(environment.getProperty("ssl.trustStore")).thenReturn("trust.jks"); Mockito.when(environment.getProperty("ssl.trustStoreType")).thenReturn("JKS"); Mockito.when(environment.getProperty("ssl.trustStoreValue")).thenReturn("Changeme_123"); Mockito.when(environment.getProperty("ssl.keyStore")).thenReturn("server.p12"); Mockito.when(environment.getProperty("ssl.keyStoreType")).thenReturn("PKCS12"); Mockito.when(environment.getProperty("ssl.keyStoreValue")).thenReturn("Changeme_123"); Mockito.when(environment.getProperty("ssl.crl")).thenReturn("revoke.crl"); } @Test public void testSSLOption() { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); String protocols = option.getProtocols(); option.setProtocols(protocols); Assertions.assertEquals("TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); String ciphers = option.getCiphers(); option.setCiphers(ciphers); Assertions.assertEquals( "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SH" + "A,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA", ciphers); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); Assertions.assertTrue(authPeer); boolean checkCNHost = option.isCheckCNHost(); option.setCheckCNHost(checkCNHost); Assertions.assertTrue(checkCNHost); boolean checkCNWhite = option.isCheckCNWhite(); option.setCheckCNWhite(checkCNWhite); Assertions.assertTrue(checkCNWhite); String checkCNWhiteFile = option.getCheckCNWhiteFile(); option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); boolean allowRenegotiate = option.isAllowRenegotiate(); option.setAllowRenegotiate(allowRenegotiate); Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); Assertions.assertEquals("internal", storePath); String trustStore = option.getTrustStore(); option.setTrustStore(trustStore); Assertions.assertEquals("trust.jks", trustStore); String trustStoreType = option.getTrustStoreType(); option.setTrustStoreType(trustStoreType); Assertions.assertEquals("JKS", trustStoreType); String trustStoreValue = option.getTrustStoreValue(); option.setTrustStoreValue(trustStoreValue); Assertions.assertEquals("Changeme_123", trustStoreValue); String keyStore = option.getKeyStore(); option.setKeyStore(keyStore); Assertions.assertEquals("server.p12", keyStore); String keyStoreType = option.getKeyStoreType(); option.setKeyStoreType(keyStoreType); Assertions.assertEquals("PKCS12", keyStoreType); String keyStoreValue = option.getKeyStoreValue(); option.setKeyStoreValue(keyStoreValue); Assertions.assertEquals("Changeme_123", keyStoreValue); String crl = option.getCrl(); option.setCrl(crl); Assertions.assertEquals("revoke.crl", crl); } @Test public void testSSLOptionYaml() { Mockito.when(environment.getProperty("ssl.server.sslCustomClass")).thenReturn("wwrong"); SSLOption option = SSLOption.build("server", environment); String protocols = option.getProtocols(); option.setProtocols(protocols); Assertions.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); String ciphers = option.getCiphers(); option.setCiphers(ciphers); Assertions.assertEquals("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", ciphers); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); Assertions.assertTrue(authPeer); boolean checkCNHost = option.isCheckCNHost(); option.setCheckCNHost(checkCNHost); Assertions.assertTrue(checkCNHost); boolean checkCNWhite = option.isCheckCNWhite(); option.setCheckCNWhite(checkCNWhite); Assertions.assertTrue(checkCNWhite); String checkCNWhiteFile = option.getCheckCNWhiteFile(); option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); boolean allowRenegotiate = option.isAllowRenegotiate(); option.setAllowRenegotiate(allowRenegotiate); Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); Assertions.assertEquals("internal", storePath); String trustStore = option.getTrustStore(); option.setTrustStore(trustStore); Assertions.assertEquals("trust.jks", trustStore); String trustStoreType = option.getTrustStoreType(); option.setTrustStoreType(trustStoreType); Assertions.assertEquals("JKS", trustStoreType); String trustStoreValue = option.getTrustStoreValue(); option.setTrustStoreValue(trustStoreValue); Assertions.assertEquals("Changeme_123", trustStoreValue); String keyStore = option.getKeyStore(); option.setKeyStore(keyStore); Assertions.assertEquals("server.p12", keyStore); String keyStoreType = option.getKeyStoreType(); option.setKeyStoreType(keyStoreType); Assertions.assertEquals("PKCS12", keyStoreType); String keyStoreValue = option.getKeyStoreValue(); option.setKeyStoreValue(keyStoreValue); Assertions.assertEquals("Changeme_123", keyStoreValue); String crl = option.getCrl(); option.setCrl(crl); Assertions.assertEquals("revoke.crl", crl); option.setSslCustomClass("123"); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); Assertions.assertArrayEquals(custom.decode("123".toCharArray()), "123".toCharArray()); } @Test public void testSSLOptionYamlOption2() throws Exception { Mockito.when(environment.getProperty("ssl.protocols")).thenReturn("TLSv1.2"); SSLOption option = SSLOption.build("server", environment); String protocols = option.getProtocols(); option.setProtocols(protocols); Assertions.assertEquals("TLSv1.2", protocols); System.clearProperty("ssl.protocols"); } @Test public void testSSLOptionYamlOptionWithPropertyFalse() throws Exception { Mockito.when(environment.getProperty("ssl.authPeer")).thenReturn("false"); SSLOption option = SSLOption.build("server", environment); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); Assertions.assertFalse(authPeer); System.getProperties().remove("ssl.authPeer"); } @Test public void testSSLOptionYamlOptionWithPropertyTrue() throws Exception { SSLOption option = SSLOption.build("server", environment); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); Assertions.assertTrue(authPeer); System.getProperties().remove("ssl.authPeer"); } @Test public void testSSLOptionYamlOption() throws Exception { SSLOption option = SSLOption.build("server", environment); String protocols = option.getProtocols(); option.setProtocols(protocols); Assertions.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); String ciphers = option.getCiphers(); option.setCiphers(ciphers); Assertions.assertEquals( "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", ciphers); boolean authPeer = option.isAuthPeer(); option.setAuthPeer(authPeer); Assertions.assertTrue(authPeer); boolean checkCNHost = option.isCheckCNHost(); option.setCheckCNHost(checkCNHost); Assertions.assertTrue(checkCNHost); boolean checkCNWhite = option.isCheckCNWhite(); option.setCheckCNWhite(checkCNWhite); Assertions.assertTrue(checkCNWhite); String checkCNWhiteFile = option.getCheckCNWhiteFile(); option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); boolean allowRenegotiate = option.isAllowRenegotiate(); option.setAllowRenegotiate(allowRenegotiate); Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); Assertions.assertEquals("internal", storePath); String trustStore = option.getTrustStore(); option.setTrustStore(trustStore); Assertions.assertEquals("trust.jks", trustStore); String trustStoreType = option.getTrustStoreType(); option.setTrustStoreType(trustStoreType); Assertions.assertEquals("JKS", trustStoreType); String trustStoreValue = option.getTrustStoreValue(); option.setTrustStoreValue(trustStoreValue); Assertions.assertEquals("Changeme_123", trustStoreValue); String keyStore = option.getKeyStore(); option.setKeyStore(keyStore); Assertions.assertEquals("server.p12", keyStore); String keyStoreType = option.getKeyStoreType(); option.setKeyStoreType(keyStoreType); Assertions.assertEquals("PKCS12", keyStoreType); String keyStoreValue = option.getKeyStoreValue(); option.setKeyStoreValue(keyStoreValue); Assertions.assertEquals("Changeme_123", keyStoreValue); String crl = option.getCrl(); option.setCrl(crl); Assertions.assertEquals("revoke.crl", crl); option.setSslCustomClass("123"); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); Assertions.assertArrayEquals(custom.decode("123".toCharArray()), "123".toCharArray()); } @SuppressWarnings("unused") @Test public void testSSLOptionNull() { try { SSLOption option = SSLOption.build(DIR + "/servers.ssl.properties"); } catch (IllegalArgumentException e) { Assertions.assertEquals("Bad file name.", e.getMessage()); } } @Test public void testBuildException() { new MockUp() { @Mock public String getCanonicalPath() throws IOException { throw new IOException(); } }; try { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); Assertions.assertNotNull(option); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testBuildIOException() { new MockUp() { @Mock public synchronized void load(Reader reader) throws IOException { throw new IOException(); } }; boolean validAssert = true; try { SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); Assertions.assertEquals("revoke.crl", option.getCrl()); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); validAssert = false; } Assertions.assertFalse(validAssert); } @Test public void testBuildInputStream() { try { URL url = this.getClass().getResource("/server.ssl.properties"); InputStream inputStream = url.openStream(); SSLOption option = SSLOption.build(inputStream); Assertions.assertNotNull(option); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); } } @Test public void testPrivateMethodException() { SSLOption option = new SSLOption(); boolean validAssert = true; try { option.load("test"); } catch (Exception e) { Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); validAssert = false; } Assertions.assertFalse(validAssert); } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestSSLOptionFactory { Environment environment = Mockito.mock(Environment.class); @Test public void testSSLOptionFactory() { SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", environment); Assertions.assertNull(factory); } @Test public void testSSLOptionFactoryWrong(@Mocked SSLOption option) { new Expectations() { { SSLOption.getStringProperty((Environment) any, anyString, (String[]) any); result = "wrong"; } }; IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> SSLOptionFactory.createSSLOptionFactory("cc", null)); Assertions.assertEquals("Failed to create SSLOptionFactory.", exception.getMessage()); } @Test public void testSSLOptionFactoryCurrent() { new MockUp() { @Mock public String getStringProperty(Environment environment, String defaultValue, String... keys) { return "org.apache.servicecomb.foundation.ssl.MyOptionFactory"; } }; SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", environment); Assertions.assertEquals(factory.createSSLOption().getProtocols(), "TLSv1.2"); } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import javax.net.ssl.SSLSocketFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; public class TestSSLSocketFactoryExt { private SSLSocketFactoryExt instance = null; @Before public void setUp() throws Exception { SSLSocketFactory factory = Mockito.mock(SSLSocketFactoryExt.class); String[] ciphers = {"test"}; String[] protos = {"test"}; instance = new SSLSocketFactoryExt(factory, ciphers, protos); } @After public void tearDown() throws Exception { instance = null; } @Test public void testCreateSocketException() { boolean validAssert = true; try { instance.createSocket("host", 8080); } catch (Exception e) { validAssert = false; } Assertions.assertFalse(validAssert); } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestTrustAllManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.net.Socket; import java.security.cert.X509Certificate; import javax.net.ssl.SSLEngine; public class TestTrustAllManager { @Test public void testTrustAllManager() throws Exception { TrustAllManager manager = new TrustAllManager(); manager.checkClientTrusted((X509Certificate[]) null, (String) null); manager.checkServerTrusted((X509Certificate[]) null, (String) null); manager.checkClientTrusted((X509Certificate[]) null, (String) null, (Socket) null); manager.checkClientTrusted((X509Certificate[]) null, (String) null, (SSLEngine) null); manager.checkServerTrusted((X509Certificate[]) null, (String) null, (Socket) null); manager.checkServerTrusted((X509Certificate[]) null, (String) null, (SSLEngine) null); Assertions.assertNull(manager.getAcceptedIssuers()); } } ================================================ FILE: foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TrustManagerExtTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.ssl; import java.io.IOException; import java.io.InputStreamReader; import java.math.BigInteger; import java.net.Socket; import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Set; import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; import org.junit.After; import org.junit.Before; import org.junit.Test; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TrustManagerExtTest { final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); static class MyX509ExtendedTrustManager extends X509ExtendedTrustManager { public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString, Socket paramSocket) throws CertificateException { } public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { } public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString, Socket paramSocket) throws CertificateException { } public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { } public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString, SSLEngine paramSSLEngine) throws CertificateException { } public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString, SSLEngine paramSSLEngine) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } } @SuppressWarnings("deprecation") static class MyX509Certificate extends X509Certificate { private static final long serialVersionUID = -3585440601605666276L; public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { } @Override public boolean hasUnsupportedCriticalExtension() { return false; } @Override public Set getCriticalExtensionOIDs() { return null; } @Override public Set getNonCriticalExtensionOIDs() { return null; } @Override public byte[] getExtensionValue(String oid) { return null; } @Override public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { } @Override public int getVersion() { return 0; } @Override public BigInteger getSerialNumber() { return null; } @Override public Principal getIssuerDN() { return null; } @Override public Principal getSubjectDN() { return null; } @Override public Date getNotBefore() { return null; } @Override public Date getNotAfter() { return null; } @Override public byte[] getTBSCertificate() throws CertificateEncodingException { return null; } @Override public byte[] getSignature() { return null; } @Override public String getSigAlgName() { return null; } @Override public String getSigAlgOID() { return null; } @Override public byte[] getSigAlgParams() { return null; } @Override public boolean[] getIssuerUniqueID() { return null; } @Override public boolean[] getSubjectUniqueID() { return null; } @Override public boolean[] getKeyUsage() { return null; } @Override public int getBasicConstraints() { return 0; } @Override public byte[] getEncoded() throws CertificateEncodingException { return null; } @Override public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } @Override public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } @Override public String toString() { return null; } @Override public PublicKey getPublicKey() { return null; } } final SSLOption option = SSLOption.build(strFilePath + "/server.ssl.properties"); final SSLCustom custom = new SSLCustom() { @Override public String getFullPath(String filename) { return strFilePath + "/ssl/" + filename; } @Override public char[] decode(char[] encrypted) { return encrypted; } @Override public String getHost() { return "10.67.147.115"; } }; @Before public void setUp() { } @After public void tearDown() { } @SuppressWarnings("unused") @Test public void testConstructor() { String keyStoreName = custom.getFullPath(option.getKeyStore()); char[] keyStoreValue = custom.decode(option.getKeyStoreValue().toCharArray()); String trustStoreName = custom.getFullPath(option.getTrustStore()); char[] trustStoreValue = custom.decode(option.getTrustStoreValue().toCharArray()); KeyStore trustStore = KeyStoreUtil.createKeyStore(trustStoreName, option.getTrustStoreType(), trustStoreValue); TrustManager[] trustManager = KeyStoreUtil.createTrustManagers(trustStore); TrustManagerExt trustManagerExt = new TrustManagerExt((X509ExtendedTrustManager) trustManager[0], option, custom); Assertions.assertEquals(3, trustManagerExt.getAcceptedIssuers()[0].getVersion()); Assertions.assertNotNull(trustManagerExt); } @Test public void testConstructorWithParam() { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; myX509CertificateArray[0] = myX509Certificate1; myX509CertificateArray[1] = myX509Certificate2; new Expectations(CertificateUtil.class) { { CertificateUtil.findOwner((X509Certificate[]) any); result = any; CertificateUtil.getCN((X509Certificate) any); result = "10.67.147.115"; } }; MyX509ExtendedTrustManager myX509ExtendedTrustManager = new MyX509ExtendedTrustManager(); TrustManagerExt trustManagerExt = new TrustManagerExt(myX509ExtendedTrustManager, option, custom); Assertions.assertNotNull(trustManagerExt); boolean validAssert = true; try { trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks"); trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks"); trustManagerExt.getAcceptedIssuers(); } catch (Exception e) { validAssert = false; } Assertions.assertTrue(validAssert); } @Test public void testCheckClientTrusted(@Mocked CertificateUtil certificateUtil) { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; myX509CertificateArray[0] = myX509Certificate1; myX509CertificateArray[1] = myX509Certificate2; new Expectations() { { CertificateUtil.findOwner((X509Certificate[]) any); result = any; CertificateUtil.getCN((X509Certificate) any); result = "10.67.147.115"; } }; MyX509ExtendedTrustManager myX509ExtendedTrustManager = new MyX509ExtendedTrustManager(); TrustManagerExt trustManagerExt = new TrustManagerExt(myX509ExtendedTrustManager, option, custom); Socket socket = null; SSLEngine sslengine = null; boolean validAssert = true; try { trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", socket); trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", sslengine); trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", socket); trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", sslengine); } catch (Exception e) { validAssert = false; } Assertions.assertTrue(validAssert); } @Test public void testCatchException(@Mocked CertificateUtil certificateUtil) { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; myX509CertificateArray[0] = myX509Certificate1; myX509CertificateArray[1] = myX509Certificate2; new Expectations() { { CertificateUtil.findOwner((X509Certificate[]) any); result = any; CertificateUtil.getCN((X509Certificate) any); result = "10.67.147.114"; } }; MyX509ExtendedTrustManager myX509ExtendedTrustManager = new MyX509ExtendedTrustManager(); TrustManagerExt trustManagerExt = new TrustManagerExt(myX509ExtendedTrustManager, option, custom); boolean validAssert = true; try { trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks"); } catch (CertificateException e) { Assertions.assertEquals("CN does not match IP: e=[10.67.147.114],t=null", e.getMessage()); validAssert = false; } Assertions.assertFalse(validAssert); } @Test public void testCheckClientTrustedException(@Mocked CertificateUtil certificateUtil) { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; myX509CertificateArray[0] = myX509Certificate1; myX509CertificateArray[1] = myX509Certificate2; new Expectations() { { CertificateUtil.findOwner((X509Certificate[]) any); result = any; CertificateUtil.getCN((X509Certificate) any); result = "10.67.147.115"; } }; MyX509ExtendedTrustManager myX509ExtendedTrustManager = new MyX509ExtendedTrustManager(); TrustManagerExt trustManagerExt = new TrustManagerExt(myX509ExtendedTrustManager, option, custom); Socket socket = null; SSLEngine sslengine = null; new MockUp() { @Mock public int read(char[] cbuf) throws IOException { throw new IOException(); } }; boolean validAssert = true; try { trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", socket); trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", sslengine); trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", socket); trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", sslengine); } catch (Exception e) { Assertions.assertEquals("java.security.cert.CertificateException", e.getClass().getName()); validAssert = false; } Assertions.assertFalse(validAssert); } } ================================================ FILE: foundations/foundation-ssl/src/test/resources/client.ssl.properties ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #########SSL options ssl.protocols=TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello ## test with extra blank ssl.ciphers=TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA ssl.authPeer=true ssl.checkCN.host=false ssl.checkCN.white=true ssl.checkCN.white.file=white.list ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal ssl.trustStore=trust.jks ssl.trustStoreType=JKS ssl.trustStoreValue=Changeme_123 ssl.keyStore=server.p12 ssl.keyStoreType=PKCS12 ssl.keyStoreValue=Changeme_123 ssl.crl=revoke.crl ================================================ FILE: foundations/foundation-ssl/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- #########SSL options ssl: protocols: TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello ciphers: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA authPeer: true checkCN.host: true checkCN.white: true checkCN.white.file: white.list allowRenegotiate: false storePath: internal trustStore: trust.jks trustStoreType: JKS trustStoreValue: Changeme_123 keyStore: server.p12 keyStoreType: PKCS12 keyStoreValue: Changeme_123 crl: revoke.crl server: sslCustomClass: wwrong ================================================ FILE: foundations/foundation-ssl/src/test/resources/server.ssl.properties ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #########SSL options ssl.protocols=TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello ssl.ciphers=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA ssl.authPeer=true ssl.checkCN.host=true ssl.checkCN.white=true ssl.checkCN.white.file=white.list ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal ssl.trustStore=trust.jks ssl.trustStoreType=JKS ssl.trustStoreValue=Changeme_123 ssl.keyStore=server.p12 ssl.keyStoreType=PKCS12 ssl.keyStoreValue=Changeme_123 ssl.crl=revoke.crl ================================================ FILE: foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #########SSL options ssl.protocols=TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello ssl.ciphers=TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA ssl.authPeer=true ssl.checkCN.host=true ssl.checkCN.white=true ssl.checkCN.white.file=white.list ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal ssl.trustStore=ssl/trust.jks ssl.trustStoreType=JKS ssl.trustStoreValue=Changeme_123 ssl.keyStore=ssl/server.p12 ssl.keyStoreType=PKCS12 ssl.keyStoreValue=Changeme_123 ssl.crl=revoke.crl ================================================ FILE: foundations/foundation-ssl/src/test/resources/ssl/server.cer ================================================ Certificate: Data: Version: 3 (0x2) Serial Number: 49:d0:cc:fe:b2:03:5e:99:00:00:00:00:00:00:0e:00 Signature Algorithm: sha256WithRSAEncryption Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Huawei Technologies Co., Ltd, OU=OSS & Service Tools Dept, CN=OSS3.0 CA Validity Not Before: Mar 31 00:00:00 2017 GMT Not After : Mar 26 00:00:00 2037 GMT Subject: C=CN, ST=GuangDong, L=ShenZhen, O=Huawei Technologies Co., Ltd, OU=OSS & Service Tools Dept, OU=development, CN=10.57.65.225 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (4096 bit) Modulus (4096 bit): 00:d1:f3:b6:1e:4d:40:d6:4e:d5:41:e6:34:b0:3f: d1:97:19:5a:1b:c0:a0:91:80:33:43:50:ab:08:df: 6b:e9:43:b4:09:ff:c9:f3:71:cf:8c:c5:61:ec:64: 26:67:24:4d:93:31:5c:1a:bf:e1:fe:c5:fb:2a:dc: 36:26:a5:52:85:b9:4e:1c:42:18:da:d0:68:86:61: dd:44:62:eb:b7:45:81:1e:3d:08:84:a1:16:06:d7: 4e:26:7b:9a:1a:19:db:eb:e8:22:25:b7:62:5c:7a: d6:d8:3d:67:f1:a9:fe:bc:b9:af:f1:f6:95:88:19: e0:89:d3:e1:05:c1:b1:c3:78:9a:99:5a:89:50:ea: 61:32:5a:9c:42:aa:2a:e9:81:a0:a7:7f:31:f6:fa: ea:1f:6a:d7:c1:2a:71:d0:d4:91:eb:86:25:fb:de: a7:ef:74:09:f9:5d:e1:33:23:35:36:67:74:60:05: e6:01:4f:fb:ac:b5:bb:c8:be:4a:60:a8:e1:9e:da: 5f:63:bc:de:cc:08:20:0f:67:c0:17:22:62:43:35: f1:63:32:4d:94:a0:c2:54:25:f4:78:ac:bc:8f:55: 7e:5c:30:21:0f:aa:34:39:55:9e:ab:99:1b:f1:00: dc:97:47:d7:0f:74:2d:39:16:f1:44:d7:f2:7e:3e: c0:fb:bd:41:d9:72:fa:52:1f:96:d3:3e:ad:99:3e: 09:ab:3d:20:3b:60:43:d3:51:89:7f:5e:3e:bc:38: a0:23:34:f4:2d:1a:fe:fa:a8:39:d5:b6:28:a0:ae: 35:ad:54:40:75:a8:ae:84:46:7f:1b:b3:45:80:3f: 75:38:a7:12:ea:a5:96:7e:c6:47:12:f0:88:74:16: 2b:e4:ca:0f:9d:b2:bc:9b:b7:96:68:8c:be:b0:9b: aa:bf:7f:5e:41:18:49:0e:51:da:e9:2d:6d:a7:a0: 20:94:6f:4a:7d:05:41:94:27:59:16:64:8c:8b:09: 60:c2:e7:12:72:1f:9e:66:52:08:1f:71:58:a2:c6: 90:13:31:7e:0a:4b:99:56:cc:3d:77:8c:31:19:11: 99:f5:89:60:3a:17:22:6a:a2:00:e1:57:52:83:1f: 55:23:f2:b3:9f:29:5c:5d:e9:00:69:f5:64:74:3c: 1e:9b:da:da:31:28:a5:f4:88:4f:2f:d1:e3:2d:d0: bb:0e:bb:df:6a:5a:36:55:50:04:58:15:fa:d3:08: af:ed:40:29:76:57:9e:52:eb:98:90:7d:45:fd:67: 22:07:42:c4:23:f5:e5:32:9a:82:4a:5c:49:ee:b4: a6:28:81:76:b4:a6:e0:c6:4e:dd:ad:58:06:ca:b9: 53:08:33 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: critical Digital Signature, Non Repudiation, Key Encipherment X509v3 Subject Key Identifier: 27:15:69:5B:D5:13:60:C4:5C:62:54:75:55:A1:1D:4D:57:27:03:1F X509v3 Authority Key Identifier: keyid:D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B DirName:/C=CN/ST=GuangDong/L=ShenZhen/O=Huawei Technologies Co., Ltd/OU=OSS & Service Tools Dept/CN=OSS3.0 CA serial:7D:FD:C5:D8:70:DA:6E:0F:00:00:00:00:00:00:00:00 X509v3 Subject Alternative Name: DNS:10.57.65.225, IP Address:10.57.65.225 Signature Algorithm: sha256WithRSAEncryption 38:1b:40:c9:3c:7f:63:a0:2d:64:59:2b:d9:4a:4b:db:c4:70: ca:04:f0:e4:61:d2:47:7e:66:2a:f7:2f:15:46:f5:9c:8b:0b: c0:97:f1:b4:e9:93:b0:d4:62:d5:22:a5:83:75:be:3a:9d:cc: 79:42:ec:85:11:17:c5:a2:e0:c6:30:64:3f:e2:4a:c1:9c:bd: 47:1d:8c:38:e1:2e:06:fe:08:c4:06:65:cb:31:49:a1:6c:5d: c5:f4:68:26:66:c8:b2:c6:40:24:01:db:de:0d:fe:1a:f3:10: 07:9b:9c:82:bc:b4:32:6a:7a:19:d7:c1:1a:cb:88:ba:de:e3: 15:0c:16:92:ed:61:f0:1e:ca:e3:52:0f:75:be:42:fc:4e:2e: 3d:ca:8c:70:03:a9:03:c6:6b:9b:d6:7a:de:75:c0:dd:d4:d8: a6:03:92:5c:76:94:ab:22:dd:4e:a7:52:47:c3:79:46:4e:37: 30:a8:4a:5f:4a:ae:3e:c4:cd:43:d0:fa:4b:40:60:98:b9:53: c8:46:3e:22:bc:63:39:30:1c:6f:c7:5e:2c:ce:fc:9f:24:c1: 49:a4:6f:ca:b9:72:44:ee:67:f1:f9:59:6a:1a:09:a8:df:64: 60:57:c0:88:36:c6:73:5c:fc:1a:00:d9:38:17:d8:85:84:c5: 31:88:45:66:ed:f6:fd:98:24:4d:de:68:d9:a0:87:7a:3f:ec: 62:ba:d9:16:7e:ef:67:88:bd:c6:b3:70:72:c9:af:21:45:37: b5:d1:84:b7:8b:1a:eb:5c:5f:07:a8:6b:d0:ab:01:56:87:e3: c5:a4:b8:cc:d7:d0:47:38:60:97:85:b5:42:74:12:9e:77:03: 19:a7:77:fb:f9:f9:6f:cc:c6:ff:4d:be:65:28:17:a6:45:19: da:12:5f:15:de:77:06:96:43:0e:19:50:7f:82:aa:da:34:78: d8:ca:8b:8e:ba:1d:20:15:f7:89:15:c0:09:26:76:4d:a6:33: 4a:ed:a1:5d:be:78:54:18:f3:62:f2:7f:a5:bd:22:db:b1:49: 32:87:01:fc:a8:f3:8f:fd:3d:fd:9c:59:aa:b5:8a:d3:a4:49: 19:95:ae:6c:24:e4:98:32:92:07:04:e6:4a:33:8c:af:8e:d7: 05:64:3a:1e:c6:93:85:89:6b:9f:70:c4:f6:32:fa:bb:c3:53: 9c:e2:73:ec:09:6a:d8:8f:23:a1:0f:f1:03:f4:cf:cf:8d:6c: 00:37:bb:53:43:9a:7e:99:27:95:ef:05:52:11:d3:0e:a9:d6: e9:88:ab:c2:e9:57:09:95:e6:1c:01:5b:f2:a6:d4:fa:44:57: 40:e6:eb:4c:9c:75:12:8b -----BEGIN CERTIFICATE----- MIIG+TCCBOGgAwIBAgIQSdDM/rIDXpkAAAAAAAAOADANBgkqhkiG9w0BAQsFADCB kjELMAkGA1UEBhMCQ04xEjAQBgNVBAgTCUd1YW5nRG9uZzERMA8GA1UEBxMIU2hl blpoZW4xJTAjBgNVBAoTHEh1YXdlaSBUZWNobm9sb2dpZXMgQ28uLCBMdGQxITAf BgNVBAsUGE9TUyAmIFNlcnZpY2UgVG9vbHMgRGVwdDESMBAGA1UEAxMJT1NTMy4w IENBMB4XDTE3MDMzMTAwMDAwMFoXDTM3MDMyNjAwMDAwMFowgasxCzAJBgNVBAYT AkNOMRIwEAYDVQQIEwlHdWFuZ0RvbmcxETAPBgNVBAcTCFNoZW5aaGVuMSUwIwYD VQQKExxIdWF3ZWkgVGVjaG5vbG9naWVzIENvLiwgTHRkMSEwHwYDVQQLFBhPU1Mg JiBTZXJ2aWNlIFRvb2xzIERlcHQxFDASBgNVBAsTC2RldmVsb3BtZW50MRUwEwYD VQQDEwwxMC41Ny42NS4yMjUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC AQDR87YeTUDWTtVB5jSwP9GXGVobwKCRgDNDUKsI32vpQ7QJ/8nzcc+MxWHsZCZn JE2TMVwav+H+xfsq3DYmpVKFuU4cQhja0GiGYd1EYuu3RYEePQiEoRYG104me5oa Gdvr6CIlt2JcetbYPWfxqf68ua/x9pWIGeCJ0+EFwbHDeJqZWolQ6mEyWpxCqirp gaCnfzH2+uofatfBKnHQ1JHrhiX73qfvdAn5XeEzIzU2Z3RgBeYBT/ustbvIvkpg qOGe2l9jvN7MCCAPZ8AXImJDNfFjMk2UoMJUJfR4rLyPVX5cMCEPqjQ5VZ6rmRvx ANyXR9cPdC05FvFE1/J+PsD7vUHZcvpSH5bTPq2ZPgmrPSA7YEPTUYl/Xj68OKAj NPQtGv76qDnVtiigrjWtVEB1qK6ERn8bs0WAP3U4pxLqpZZ+xkcS8Ih0Fivkyg+d srybt5ZojL6wm6q/f15BGEkOUdrpLW2noCCUb0p9BUGUJ1kWZIyLCWDC5xJyH55m UggfcViixpATMX4KS5lWzD13jDEZEZn1iWA6FyJqogDhV1KDH1Uj8rOfKVxd6QBp 9WR0PB6b2toxKKX0iE8v0eMt0LsOu99qWjZVUARYFfrTCK/tQCl2V55S65iQfUX9 ZyIHQsQj9eUymoJKXEnutKYogXa0puDGTt2tWAbKuVMIMwIDAQABo4IBLjCCASow CQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYEFCcVaVvVE2DEXGJU dVWhHU1XJwMfMIHOBgNVHSMEgcYwgcOAFNVOgghwFmM934clC30u64MharpboYGY pIGVMIGSMQswCQYDVQQGEwJDTjESMBAGA1UECBMJR3VhbmdEb25nMREwDwYDVQQH EwhTaGVuWmhlbjElMCMGA1UEChMcSHVhd2VpIFRlY2hub2xvZ2llcyBDby4sIEx0 ZDEhMB8GA1UECxQYT1NTICYgU2VydmljZSBUb29scyBEZXB0MRIwEAYDVQQDEwlP U1MzLjAgQ0GCEH39xdhw2m4PAAAAAAAAAAAwHQYDVR0RBBYwFIIMMTAuNTcuNjUu MjI1hwQKOUHhMA0GCSqGSIb3DQEBCwUAA4ICAQA4G0DJPH9joC1kWSvZSkvbxHDK BPDkYdJHfmYq9y8VRvWciwvAl/G06ZOw1GLVIqWDdb46ncx5QuyFERfFouDGMGQ/ 4krBnL1HHYw44S4G/gjEBmXLMUmhbF3F9GgmZsiyxkAkAdveDf4a8xAHm5yCvLQy anoZ18Eay4i63uMVDBaS7WHwHsrjUg91vkL8Ti49yoxwA6kDxmub1nredcDd1Nim A5JcdpSrIt1Op1JHw3lGTjcwqEpfSq4+xM1D0PpLQGCYuVPIRj4ivGM5MBxvx14s zvyfJMFJpG/KuXJE7mfx+VlqGgmo32RgV8CINsZzXPwaANk4F9iFhMUxiEVm7fb9 mCRN3mjZoId6P+xiutkWfu9niL3Gs3Byya8hRTe10YS3ixrrXF8HqGvQqwFWh+PF pLjM19BHOGCXhbVCdBKedwMZp3f7+flvzMb/Tb5lKBemRRnaEl8V3ncGlkMOGVB/ gqraNHjYyouOuh0gFfeJFcAJJnZNpjNK7aFdvnhUGPNi8n+lvSLbsUkyhwH8qPOP /T39nFmqtYrTpEkZla5sJOSYMpIHBOZKM4yvjtcFZDoexpOFiWufcMT2Mvq7w1Oc 4nPsCWrYjyOhD/ED9M/PjWwAN7tTQ5p+mSeV7wVSEdMOqdbpiKvC6VcJleYcAVvy ptT6RFdA5utMnHUSiw== -----END CERTIFICATE----- ================================================ FILE: foundations/foundation-ssl/src/test/resources/ssl/trust.cer ================================================ Certificate: Data: Version: 3 (0x2) Serial Number: 7d:fd:c5:d8:70:da:6e:0f:00:00:00:00:00:00:00:00 Signature Algorithm: sha256WithRSAEncryption Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=Huawei Technologies Co., Ltd, OU=OSS & Service Tools Dept, CN=OSS3.0 CA Validity Not Before: Nov 1 00:00:00 2015 GMT Not After : Oct 25 00:00:00 2040 GMT Subject: C=CN, ST=GuangDong, L=ShenZhen, O=Huawei Technologies Co., Ltd, OU=OSS & Service Tools Dept, CN=OSS3.0 CA Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (4096 bit) Modulus (4096 bit): 00:a3:9d:7e:e8:7b:98:9a:d2:7f:fc:7f:4c:cc:79: d1:80:fa:1d:77:6f:dd:39:83:4d:4a:4a:2d:49:9f: e4:e8:ca:3a:7b:89:fa:1c:fc:17:0c:4a:bb:6e:6d: 12:cf:e8:d8:bf:e0:70:b7:0f:ca:fc:9d:d6:26:6d: ae:92:47:70:ad:33:e3:de:50:f9:6f:9a:75:84:c8: 10:a0:46:b6:4b:37:a3:68:ba:bc:86:86:d4:60:44: 2c:38:38:0e:fb:24:ce:e5:53:4b:6b:42:b6:9e:de: 12:f1:25:21:73:f3:87:d0:cc:bb:6b:fb:e6:b3:53: 21:b3:38:2e:55:90:46:0e:07:3e:48:41:35:3c:13: f0:69:fd:24:b0:cd:4e:1d:d7:ed:66:80:00:01:ff: 31:1c:48:d1:36:1f:82:82:b6:ed:57:c0:b0:af:cd: 7b:55:57:eb:93:8a:62:e0:74:5d:5c:1e:76:98:f9: 3b:f2:21:c0:5c:08:dd:40:fe:c9:d2:8e:26:69:93: 10:cd:bc:1f:48:30:47:d5:5a:0d:4e:3d:24:ca:c5: c0:72:14:08:38:db:df:03:6c:43:87:c0:bb:70:15: e7:55:87:b5:53:18:77:e0:78:46:50:c8:74:78:ba: 19:7f:bf:84:7e:fb:87:d9:a1:6c:bd:53:8f:13:4f: b3:0c:23:66:7b:69:4e:a3:64:de:48:72:76:37:a2: 86:58:74:7f:dd:77:b1:b0:68:93:9c:cf:6b:00:b9: 0f:10:76:bf:9f:3f:8e:86:3d:da:4b:d2:7b:b1:db: f1:59:81:b7:87:0b:40:a5:38:ad:91:cd:1a:4c:d9: 79:59:e7:54:40:06:68:68:de:6b:8b:54:fd:84:e3: 89:35:fa:bd:8c:1d:0f:4e:0d:a6:ba:39:f8:bd:e6: 88:ca:a0:aa:d7:92:4d:14:75:09:af:bc:10:e1:98: c5:fa:40:28:bc:04:0d:b0:ad:63:92:eb:27:94:e9: a8:65:c9:6f:b3:ab:52:c6:8e:87:2f:64:de:d6:11: 69:77:5c:ce:1e:31:45:fb:6f:b4:72:01:ce:2c:c8: 4d:74:f2:48:28:1a:51:a3:14:50:b1:81:13:7a:4b: d8:05:e5:98:00:78:c9:2f:a2:46:24:a9:8f:9a:b0: e0:42:78:80:73:da:cb:7f:50:3c:c5:b9:1b:3c:7f: b6:e1:c0:cf:c6:f0:94:80:8c:64:87:d5:d4:4e:e4: 53:c8:6e:74:0a:f5:e8:b2:a6:56:11:b4:da:14:46: 82:91:7e:63:8e:58:9f:2d:15:41:dc:d0:81:4d:d8: 40:bd:ca:6a:67:42:b5:5b:87:24:70:95:ce:a4:61: 05:60:4b Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:TRUE X509v3 Key Usage: critical Certificate Sign, CRL Sign X509v3 Subject Key Identifier: D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B X509v3 Authority Key Identifier: keyid:D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B DirName:/C=CN/ST=GuangDong/L=ShenZhen/O=Huawei Technologies Co., Ltd/OU=OSS & Service Tools Dept/CN=OSS3.0 CA serial:7D:FD:C5:D8:70:DA:6E:0F:00:00:00:00:00:00:00:00 X509v3 Subject Alternative Name: email:oss3ca@huawei.com Signature Algorithm: sha256WithRSAEncryption 8a:52:5f:99:d0:7b:a0:8c:2d:e8:6c:ff:7a:bc:89:64:03:d4: 4d:64:7f:91:65:e4:0c:97:2b:0d:44:c4:b0:74:cc:9c:88:03: 96:51:db:7c:94:cf:a0:42:23:7d:34:f4:50:be:b1:3d:b6:c3: 30:ce:a1:59:f5:f2:76:b0:1e:c3:e2:4a:62:40:54:3e:5e:f3: f1:5f:d9:19:30:27:e7:4d:42:84:b7:14:a3:cc:be:95:9d:77: b2:35:0d:5e:0b:47:07:2c:18:a9:00:be:37:f9:a5:ba:0b:46: 4d:0f:30:a7:04:73:3c:ff:f5:a9:80:24:44:81:83:5c:15:d7: 37:cf:ab:aa:81:68:22:4f:b9:a6:e2:c5:2f:d7:12:44:9a:0d: be:a9:dc:74:80:07:fe:19:5c:bd:1c:0c:4c:da:13:c4:89:1b: 38:29:67:83:57:2d:83:77:4c:9c:77:65:79:46:4b:80:7f:85: 14:8a:84:7c:12:89:55:54:44:48:17:da:4d:5d:04:f3:8f:5b: 1c:7e:11:7c:d8:27:16:8d:13:e8:e6:e6:25:49:3f:e2:77:0e: f9:9c:3a:51:fb:8e:90:60:25:a9:4f:51:21:58:ac:ab:9f:d0: cc:fb:1d:11:fc:14:a6:0b:47:76:ab:79:7d:e2:27:80:25:9a: 6c:df:e1:1a:ca:e2:14:5d:82:2c:94:bb:0b:d1:ac:09:96:75: da:60:02:97:3c:f9:db:53:29:42:07:65:1e:36:f4:df:c1:fe: 29:b6:33:97:fb:94:b5:e8:a3:ec:98:da:ed:a2:62:46:7b:31: 75:cb:76:b5:63:b7:2f:14:da:c8:1e:5c:1e:8e:17:b2:08:ad: 71:93:e0:d9:a1:53:49:89:32:41:80:01:0e:a9:da:52:72:e3: 8d:75:b6:fc:51:04:35:8a:ec:bc:7d:b3:d3:c5:30:d6:71:4f: c0:d0:3e:79:02:4d:38:7a:17:13:7c:39:d3:e7:cb:99:72:c1: 76:fd:bf:a2:7a:14:e6:d9:48:f6:28:31:98:af:ab:04:ca:78: 35:26:51:11:04:f3:ad:0d:33:38:00:14:3d:07:6c:00:cc:9f: f7:ef:ec:e1:e9:1e:4a:1f:27:22:c2:f3:b9:e8:58:7f:54:80: d2:66:c4:01:2b:d2:c2:19:f0:da:10:3d:e9:3a:0e:d5:76:d5: ca:1f:ea:4d:84:b1:d6:ee:4a:c9:bd:4a:fd:72:68:97:75:8e: f6:7d:95:35:0b:ab:72:42:d3:5c:10:bc:d9:44:e9:dd:1c:a1: be:26:04:e4:10:67:45:b4:96:88:77:e3:1d:a1:d0:dc:60:73: 75:c0:f7:ac:4b:4b:47:8e -----BEGIN CERTIFICATE----- MIIG4jCCBMqgAwIBAgIQff3F2HDabg8AAAAAAAAAADANBgkqhkiG9w0BAQsFADCB kjELMAkGA1UEBhMCQ04xEjAQBgNVBAgTCUd1YW5nRG9uZzERMA8GA1UEBxMIU2hl blpoZW4xJTAjBgNVBAoTHEh1YXdlaSBUZWNobm9sb2dpZXMgQ28uLCBMdGQxITAf BgNVBAsUGE9TUyAmIFNlcnZpY2UgVG9vbHMgRGVwdDESMBAGA1UEAxMJT1NTMy4w IENBMB4XDTE1MTEwMTAwMDAwMFoXDTQwMTAyNTAwMDAwMFowgZIxCzAJBgNVBAYT AkNOMRIwEAYDVQQIEwlHdWFuZ0RvbmcxETAPBgNVBAcTCFNoZW5aaGVuMSUwIwYD VQQKExxIdWF3ZWkgVGVjaG5vbG9naWVzIENvLiwgTHRkMSEwHwYDVQQLFBhPU1Mg JiBTZXJ2aWNlIFRvb2xzIERlcHQxEjAQBgNVBAMTCU9TUzMuMCBDQTCCAiIwDQYJ KoZIhvcNAQEBBQADggIPADCCAgoCggIBAKOdfuh7mJrSf/x/TMx50YD6HXdv3TmD TUpKLUmf5OjKOnuJ+hz8FwxKu25tEs/o2L/gcLcPyvyd1iZtrpJHcK0z495Q+W+a dYTIEKBGtks3o2i6vIaG1GBELDg4DvskzuVTS2tCtp7eEvElIXPzh9DMu2v75rNT IbM4LlWQRg4HPkhBNTwT8Gn9JLDNTh3X7WaAAAH/MRxI0TYfgoK27VfAsK/Ne1VX 65OKYuB0XVwedpj5O/IhwFwI3UD+ydKOJmmTEM28H0gwR9VaDU49JMrFwHIUCDjb 3wNsQ4fAu3AV51WHtVMYd+B4RlDIdHi6GX+/hH77h9mhbL1TjxNPswwjZntpTqNk 3khydjeihlh0f913sbBok5zPawC5DxB2v58/joY92kvSe7Hb8VmBt4cLQKU4rZHN GkzZeVnnVEAGaGjea4tU/YTjiTX6vYwdD04Npro5+L3miMqgqteSTRR1Ca+8EOGY xfpAKLwEDbCtY5LrJ5TpqGXJb7OrUsaOhy9k3tYRaXdczh4xRftvtHIBzizITXTy SCgaUaMUULGBE3pL2AXlmAB4yS+iRiSpj5qw4EJ4gHPay39QPMW5Gzx/tuHAz8bw lICMZIfV1E7kU8hudAr16LKmVhG02hRGgpF+Y45Yny0VQdzQgU3YQL3KamdCtVuH JHCVzqRhBWBLAgMBAAGjggEwMIIBLDAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQE AwIBBjAdBgNVHQ4EFgQU1U6CCHAWYz3fhyULfS7rgyFqulswgc4GA1UdIwSBxjCB w4AU1U6CCHAWYz3fhyULfS7rgyFquluhgZikgZUwgZIxCzAJBgNVBAYTAkNOMRIw EAYDVQQIEwlHdWFuZ0RvbmcxETAPBgNVBAcTCFNoZW5aaGVuMSUwIwYDVQQKExxI dWF3ZWkgVGVjaG5vbG9naWVzIENvLiwgTHRkMSEwHwYDVQQLFBhPU1MgJiBTZXJ2 aWNlIFRvb2xzIERlcHQxEjAQBgNVBAMTCU9TUzMuMCBDQYIQff3F2HDabg8AAAAA AAAAADAcBgNVHREEFTATgRFvc3MzY2FAaHVhd2VpLmNvbTANBgkqhkiG9w0BAQsF AAOCAgEAilJfmdB7oIwt6Gz/eryJZAPUTWR/kWXkDJcrDUTEsHTMnIgDllHbfJTP oEIjfTT0UL6xPbbDMM6hWfXydrAew+JKYkBUPl7z8V/ZGTAn501ChLcUo8y+lZ13 sjUNXgtHBywYqQC+N/mlugtGTQ8wpwRzPP/1qYAkRIGDXBXXN8+rqoFoIk+5puLF L9cSRJoNvqncdIAH/hlcvRwMTNoTxIkbOClng1ctg3dMnHdleUZLgH+FFIqEfBKJ VVRESBfaTV0E849bHH4RfNgnFo0T6ObmJUk/4ncO+Zw6UfuOkGAlqU9RIVisq5/Q zPsdEfwUpgtHdqt5feIngCWabN/hGsriFF2CLJS7C9GsCZZ12mAClzz521MpQgdl Hjb038H+KbYzl/uUteij7Jja7aJiRnsxdct2tWO3LxTayB5cHo4XsgitcZPg2aFT SYkyQYABDqnaUnLjjXW2/FEENYrsvH2z08Uw1nFPwNA+eQJNOHoXE3w50+fLmXLB dv2/onoU5tlI9igxmK+rBMp4NSZREQTzrQ0zOAAUPQdsAMyf9+/s4ekeSh8nIsLz uehYf1SA0mbEASvSwhnw2hA96ToO1XbVyh/qTYSx1u5Kyb1K/XJol3WO9n2VNQur ckLTXBC82UTp3RyhviYE5BBnRbSWiHfjHaHQ3GBzdcD3rEtLR44= -----END CERTIFICATE----- ================================================ FILE: foundations/foundation-ssl/src/test/resources/ssl/white.list ================================================ 127.0.0.1 10.67.190.60 10.67.147.115 10.57.65.225 ================================================ FILE: foundations/foundation-test-scaffolding/pom.xml ================================================ 4.0.0 org.apache.servicecomb foundations 3.4.0-SNAPSHOT foundation-test-scaffolding Java Chassis::Foundations::Test Scaffolding org.springframework spring-context io.vertx vertx-web io.vertx vertx-codegen provided com.google.guava guava org.apache.logging.log4j log4j-core provided true org.apache.logging.log4j log4j-api provided true com.fasterxml.jackson.core jackson-annotations javax.inject javax.inject org.springframework spring-aspects io.swagger.core.v3 swagger-core-jakarta provided org.assertj assertj-core compile ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/AssertUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding; import static org.assertj.core.api.Assertions.assertThat; import org.assertj.core.api.AbstractStringAssert; import io.netty.handler.codec.string.LineSeparator; import io.vertx.core.json.Json; public interface AssertUtils { static AbstractStringAssert assertPrettyJson(Object value) { String json = Json.encodePrettily(value) .replace(LineSeparator.WINDOWS.value(), LineSeparator.UNIX.value()); return assertThat(json); } } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/exception/RuntimeExceptionWithoutStackTrace.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.exception; public class RuntimeExceptionWithoutStackTrace extends RuntimeException { private static final long serialVersionUID = -1L; public RuntimeExceptionWithoutStackTrace() { } public RuntimeExceptionWithoutStackTrace(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { return this; } } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.log; import java.io.Closeable; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.layout.PatternLayout; public class LogCollector implements Closeable { List events = new ArrayList<>(); Appender appender; String appenderName; public LogCollector() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); appenderName = "LogCollector" + System.nanoTime(); appender = new AbstractAppender(appenderName, null, PatternLayout.createDefaultLayout(), true, Property.EMPTY_ARRAY) { @Override public void append(LogEvent event) { events.add(event); } }; appender.start(); config.getRootLogger().addAppender(appender, Level.ALL, null); ctx.updateLoggers(config); } public LogCollector setLogLevel(String logName, Level level) { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); config.getLoggerConfig(logName).setLevel(level); return this; } public List getEvents() { return events; } public LogEvent getEvent(int index) { return events.get(index); } public LogEvent getLastEvents() { return events.get(events.size() - 1); } public List getThrowables() { return events.stream() .map(LogEvent::getThrown) .filter(Objects::nonNull) .collect(Collectors.toList()); } public List getThrowableMessages() { return events.stream() .filter(e -> e.getThrown() != null) .map(e -> e.getThrown().getMessage()) .collect(Collectors.toList()); } public void tearDown() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); appender.stop(); config.getRootLogger().removeAppender(appenderName); ctx.updateLoggers(config); } public void clear() { events = new ArrayList<>(); } @Override public void close() { tearDown(); } } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Color.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.model; public enum Color { RED, YELLOW, BLUE } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Empty.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.model; public class Empty { } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Media.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.model; import com.fasterxml.jackson.annotation.JsonProperty; /** * To test the situation that enum values are specified by {@link JsonProperty}. */ public enum Media { @JsonProperty("AAC") AAC, @JsonProperty FLAC, @JsonProperty("H.264") H_264, @JsonProperty("MPEG-2") MPEG_2, WMV } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/People.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.model; import java.util.List; public class People { public String name; public List friends; } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/User.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.model; import java.util.List; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; public class User { public String name; // An issue not fixed by open api // see: https://github.com/swagger-api/swagger-core/issues/3484 @ArraySchema(schema = @Schema(implementation = User.class)) public List friends; } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/spring/SpringUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.spring; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import java.util.UUID; import javax.inject.Inject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.util.StringValueResolver; public final class SpringUtils { private SpringUtils() { } public static StringValueResolver createStringValueResolver(Map map) { StandardEnvironment standardEnvironment = new StandardEnvironment(); standardEnvironment.getPropertySources().addFirst( new MapPropertySource(UUID.randomUUID().toString(), map)); return standardEnvironment::resolvePlaceholders; } public static void ensureNoInject(Class cls) { for (Field field : cls.getDeclaredFields()) { if (field.getAnnotation(Inject.class) != null) { throw new IllegalStateException(String .format("field %s:%s has %s annotation", cls.getName(), field.getName(), Inject.class.getName())); } if (field.getAnnotation(Autowired.class) != null) { throw new IllegalStateException(String .format("field %s:%s has %s annotation", cls.getName(), field.getName(), Autowired.class.getName())); } } for (Method method : cls.getDeclaredMethods()) { if (method.getAnnotation(Inject.class) != null) { throw new IllegalStateException(String .format("method %s:%s has %s annotation", cls.getName(), method.getName(), Inject.class.getName())); } if (method.getAnnotation(Autowired.class) != null) { throw new IllegalStateException(String .format("method %s:%s has %s annotation", cls.getName(), method.getName(), Autowired.class.getName())); } } } } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockClock.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.time; import java.time.Clock; import java.time.Instant; import java.time.ZoneId; public class MockClock extends Clock { private MockValues values; public MockClock() { this(0L); } public MockClock(Long... values) { this.setValues(values); } public MockClock setValues(Long... values) { this.values = new MockValues() .setDefaultValue(0L) .setValues(values); return this; } @Override public ZoneId getZone() { return null; } @Override public Clock withZone(ZoneId zone) { return null; } @Override public Instant instant() { return null; } @Override public long millis() { return values.read(); } } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockTicker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.time; import com.google.common.base.Ticker; public class MockTicker extends Ticker { private MockValues values; public MockTicker() { this(0L); } public MockTicker(Long... values) { this.setValues(values); } public MockTicker setValues(Long... values) { this.values = new MockValues() .setDefaultValue(0L) .setValues(values); return this; } @Override public long read() { return values.read(); } } ================================================ FILE: foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockValues.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.test.scaffolding.time; public class MockValues { private T defaultValue; private T[] values; private int index; public MockValues setDefaultValue(T defaultValue) { this.defaultValue = defaultValue; return this; } public MockValues setValues(T[] values) { this.values = values; this.index = 0; return this; } public T read() { if (values == null || values.length == 0) { return defaultValue; } if (index >= values.length) { return values[values.length - 1]; } T value = values[index]; index++; return value; } } ================================================ FILE: foundations/foundation-vertx/pom.xml ================================================ 4.0.0 org.apache.servicecomb foundations 3.4.0-SNAPSHOT foundation-vertx Java Chassis::Foundations::Vertx io.vertx vertx-core io.vertx vertx-web org.apache.servicecomb foundation-ssl org.apache.servicecomb foundation-common io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test org.apache.servicecomb foundation-test-scaffolding ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.dns.AddressResolverOptions; public class AddressResolverConfig { private static final Logger LOGGER = LoggerFactory.getLogger(AddressResolverConfig.class); /** * get the target endpoints with custom address resolve config * @param tag config tag, such as sc.consumer or cc.consumer * @return AddressResolverOptions */ public static AddressResolverOptions getAddressResolverOptions(String tag) { AddressResolverOptions addressResolverOptions = new AddressResolverOptions(); addressResolverOptions .setServers(getStringListProperty( AddressResolverOptions.DEFAULT_SERVERS, "addressResolver." + tag + ".servers", "addressResolver.servers")); addressResolverOptions .setOptResourceEnabled(getBooleanProperty( AddressResolverOptions.DEFAULT_OPT_RESOURCE_ENABLED, "addressResolver." + tag + ".optResourceEnabled", "addressResolver.optResourceEnabled")); addressResolverOptions .setCacheMinTimeToLive(getPositiveIntProperty( AddressResolverOptions.DEFAULT_CACHE_MIN_TIME_TO_LIVE, "addressResolver." + tag + ".cacheMinTimeToLive", "addressResolver.cacheMinTimeToLive")); addressResolverOptions .setCacheMaxTimeToLive(getPositiveIntProperty( AddressResolverOptions.DEFAULT_CACHE_MAX_TIME_TO_LIVE, "addressResolver." + tag + ".cacheMaxTimeToLive", "addressResolver.cacheMaxTimeToLive")); addressResolverOptions .setCacheNegativeTimeToLive(getPositiveIntProperty( AddressResolverOptions.DEFAULT_CACHE_NEGATIVE_TIME_TO_LIVE, "addressResolver." + tag + ".cacheNegativeTimeToLive", "addressResolver.cacheNegativeTimeToLive")); addressResolverOptions .setQueryTimeout(getPositiveIntProperty( AddressResolverOptions.DEFAULT_QUERY_TIMEOUT, "addressResolver." + tag + ".queryTimeout", "addressResolver.queryTimeout")); addressResolverOptions .setMaxQueries(getPositiveIntProperty( AddressResolverOptions.DEFAULT_MAX_QUERIES, "addressResolver." + tag + ".maxQueries", "addressResolver.maxQueries")); addressResolverOptions .setRdFlag(getBooleanProperty( AddressResolverOptions.DEFAULT_RD_FLAG, "addressResolver." + tag + ".rdFlag", "addressResolver.rdFlag")); addressResolverOptions .setSearchDomains(getStringListProperty( AddressResolverOptions.DEFAULT_SEARCH_DOMAINS, "addressResolver." + tag + ".searchDomains", "addressResolver.searchDomains")); addressResolverOptions .setNdots(getPositiveIntProperty( AddressResolverOptions.DEFAULT_NDOTS, "addressResolver." + tag + ".ndots", "addressResolver.ndots")); addressResolverOptions .setRotateServers(getBooleanProperty( AddressResolverOptions.DEFAULT_ROTATE_SERVERS, "addressResolver." + tag + ".rotateServers", "addressResolver.rotateServers")); return addressResolverOptions; } private static List getStringListProperty(List defaultValue, String... keys) { for (String key : keys) { String[] vals = LegacyPropertyFactory.getProperty(key, String[].class); if (vals != null && vals.length > 0) { return Arrays.asList(vals); } } return defaultValue; } private static int getPositiveIntProperty(int defaultValue, String... keys) { for (String key : keys) { Integer val = LegacyPropertyFactory.getProperty(key, Integer.class); if (val != null && val <= 0) { LOGGER.warn("Address resolver key:{}'s value:{} is not positive, please check!", key, val); continue; } if (val != null) { return val; } } return defaultValue; } private static boolean getBooleanProperty(boolean defaultValue, String... keys) { for (String key : keys) { Boolean val = LegacyPropertyFactory.getProperty(key, Boolean.class); if (val != null) { return val; } } return defaultValue; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AsyncResultCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; public interface AsyncResultCallback extends Handler> { default void success(T data) { handle(Future.succeededFuture(data)); } default void fail(Throwable e) { handle(Future.failedFuture(e)); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/ConnectionEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; public enum ConnectionEvent { Connected, Closed } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import org.apache.servicecomb.foundation.vertx.metrics.DefaultVertxMetricsFactory; import org.apache.servicecomb.foundation.vertx.metrics.MetricsOptionsEx; import org.springframework.core.env.Environment; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.shareddata.Shareable; public class SharedVertxFactory { static class SharedVertxInfo implements Shareable { public VertxOptions vertxOptions = new VertxOptions(); public DefaultVertxMetricsFactory metricsFactory = new DefaultVertxMetricsFactory(); public MetricsOptionsEx metricsOptionsEx = (MetricsOptionsEx) metricsFactory.newOptions(); public SharedVertxInfo(Environment environment) { vertxOptions.setMetricsOptions(metricsOptionsEx); vertxOptions.setEventLoopPoolSize(readEventLoopPoolSize(environment, "servicecomb.transport.eventloop.size")); } private static int readEventLoopPoolSize(Environment environment, String key) { int count = environment.getProperty(key, int.class, -1); if (count > 0) { return count; } return VertxOptions.DEFAULT_EVENT_LOOP_POOL_SIZE; } } private static final String LOCAL_MAP_NAME = "scb"; private static final String INFO = "transport-vertx-info"; public static DefaultVertxMetricsFactory getMetricsFactory(Environment environment) { SharedVertxInfo info = (SharedVertxInfo) getSharedVertx(environment).sharedData().getLocalMap(LOCAL_MAP_NAME) .get(INFO); return info.metricsFactory; } public static Vertx getSharedVertx(Environment environment) { return VertxUtils.getVertxMap().computeIfAbsent("transport", key -> createSharedVertx(environment, key)); } private static Vertx createSharedVertx(Environment environment, String name) { SharedVertxInfo info = new SharedVertxInfo(environment); Vertx vertx = VertxUtils.init(name, info.vertxOptions, info.metricsFactory); info.metricsFactory.setVertx(vertx, info.vertxOptions); vertx.sharedData().getLocalMap(LOCAL_MAP_NAME).put(INFO, info); return vertx; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleBodyHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import jakarta.ws.rs.core.Response.Status; import org.apache.http.HttpHeaders; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.impl.BodyHandlerImpl; /** * 扩展的BodyHandler * 只支持指定ContentType格式的body数据,关闭了文件上传能力 */ public abstract class SimpleBodyHandler extends BodyHandlerImpl { @Override public void handle(RoutingContext context) { if (this.checkContentType(context)) { super.handle(context); } } protected boolean checkContentType(RoutingContext context) { String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); if (contentTypeSupported(contentType)) { return true; } Status status = Status.UNSUPPORTED_MEDIA_TYPE; context.response().setStatusCode(status.getStatusCode()).setStatusMessage(status.getReasonPhrase()); context.response().end(String.format("Content-Type %s is not supported", contentType)); return false; } protected abstract boolean contentTypeSupported(String contentType); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleJsonObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import io.vertx.core.json.JsonObject; public class SimpleJsonObject extends JsonObject { /** * 直接保存进map,规避原来的put不支持Object的问题 */ @Override public JsonObject put(String key, Object value) { getMap().put(key, value); return this; } /** * 不必复制,直接使用,规避原来的copy不支持Object的问题 */ @Override public JsonObject copy() { return this; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/TransportType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; public enum TransportType { Highway, Rest } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; public final class VertxConst { public static final String PROXY_PRE_NAME = "servicecomb.proxy."; public static final String PROXY_ENABLE = PROXY_PRE_NAME + "enable"; public static final String PROXY_HOST = PROXY_PRE_NAME + "host"; public static final String PROXY_PORT = PROXY_PRE_NAME + "port"; public static final String PROXY_USERNAME = PROXY_PRE_NAME + "username"; public static final String PROXY_PASSWD = PROXY_PRE_NAME + "passwd"; } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import java.io.File; import java.net.URL; import java.util.Arrays; import java.util.HashSet; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLManager; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.http.ClientAuth; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.WebSocketClientOptions; import io.vertx.core.net.ClientOptionsBase; import io.vertx.core.net.JksOptions; import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.OpenSSLEngineOptions; import io.vertx.core.net.PfxOptions; import io.vertx.core.net.TCPSSLOptions; public final class VertxTLSBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(VertxTLSBuilder.class); private static final String STORE_PKCS12 = "PKCS12"; private static final String STORE_JKS = "JKS"; private VertxTLSBuilder() { } public static NetServerOptions buildNetServerOptions(SSLOption sslOption, SSLCustom sslCustom, NetServerOptions netServerOptions) { buildTCPSSLOptions(sslOption, sslCustom, netServerOptions); setClientAuth(sslOption, netServerOptions); return netServerOptions; } private static void setClientAuth(SSLOption sslOption, NetServerOptions netServerOptions) { if (sslOption.isAuthPeer() || org.apache.servicecomb.foundation.ssl.ClientAuth.REQUIRED .equals(sslOption.getClientAuth())) { netServerOptions.setClientAuth(ClientAuth.REQUIRED); return; } if (org.apache.servicecomb.foundation.ssl.ClientAuth.NONE .equals(sslOption.getClientAuth())) { netServerOptions.setClientAuth(ClientAuth.NONE); return; } netServerOptions.setClientAuth(ClientAuth.REQUEST); return; } public static void buildHttpClientOptions(String sslKey, HttpClientOptions httpClientOptions) { SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, LegacyPropertyFactory.getEnvironment()); SSLOption sslOption; if (factory == null) { sslOption = SSLOption.build(sslKey, LegacyPropertyFactory.getEnvironment()); } else { sslOption = factory.createSSLOption(); } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); buildHttpClientOptions(sslOption, sslCustom, httpClientOptions); } public static void buildWebSocketClientOptions(String sslKey, WebSocketClientOptions webSocketClientOptions) { SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, LegacyPropertyFactory.getEnvironment()); SSLOption sslOption; if (factory == null) { sslOption = SSLOption.build(sslKey, LegacyPropertyFactory.getEnvironment()); } else { sslOption = factory.createSSLOption(); } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); buildWebSocketClientOptions(sslOption, sslCustom, webSocketClientOptions); } public static HttpClientOptions buildHttpClientOptions(SSLOption sslOption, SSLCustom sslCustom, HttpClientOptions httpClientOptions) { buildClientOptionsBase(sslOption, sslCustom, httpClientOptions); httpClientOptions.setVerifyHost(sslOption.isCheckCNHost()); return httpClientOptions; } public static WebSocketClientOptions buildWebSocketClientOptions(SSLOption sslOption, SSLCustom sslCustom, WebSocketClientOptions webSocketClientOptions) { buildClientOptionsBase(sslOption, sslCustom, webSocketClientOptions); webSocketClientOptions.setVerifyHost(sslOption.isCheckCNHost()); return webSocketClientOptions; } public static ClientOptionsBase buildClientOptionsBase(SSLOption sslOption, SSLCustom sslCustom, ClientOptionsBase clientOptionsBase) { buildTCPSSLOptions(sslOption, sslCustom, clientOptionsBase); if (sslOption.isAuthPeer()) { clientOptionsBase.setTrustAll(false); } else { clientOptionsBase.setTrustAll(true); } return clientOptionsBase; } private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom sslCustom, TCPSSLOptions tcpClientOptions) { tcpClientOptions.setSsl(true); if (sslOption.getEngine().equalsIgnoreCase("openssl")) { tcpClientOptions.setSslEngineOptions(new OpenSSLEngineOptions()); } String fullKeyStore = sslCustom.getFullPath(sslOption.getKeyStore()); if (isFileExists(fullKeyStore)) { if (STORE_PKCS12.equalsIgnoreCase(sslOption.getKeyStoreType())) { PfxOptions keyPfxOptions = new PfxOptions(); keyPfxOptions.setPath(fullKeyStore); keyPfxOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); tcpClientOptions.setKeyCertOptions(keyPfxOptions); } else if (STORE_JKS.equalsIgnoreCase(sslOption.getKeyStoreType())) { JksOptions keyJksOptions = new JksOptions(); keyJksOptions.setPath(fullKeyStore); keyJksOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); tcpClientOptions.setKeyCertOptions(keyJksOptions); } else { throw new IllegalArgumentException("invalid key store type."); } } else { LOGGER.warn("keyStore [" + fullKeyStore + "] file not exist, please check!"); } String fullTrustStore = sslCustom.getFullPath(sslOption.getTrustStore()); if (isFileExists(fullTrustStore)) { if (STORE_PKCS12.equalsIgnoreCase(sslOption.getTrustStoreType())) { PfxOptions trustPfxOptions = new PfxOptions(); trustPfxOptions.setPath(fullTrustStore); trustPfxOptions .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); tcpClientOptions.setTrustOptions(trustPfxOptions); } else if (STORE_JKS.equalsIgnoreCase(sslOption.getTrustStoreType())) { JksOptions trustJksOptions = new JksOptions(); trustJksOptions.setPath(fullTrustStore); trustJksOptions .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); tcpClientOptions.setTrustOptions(trustJksOptions); } else { throw new IllegalArgumentException("invalid trust store type."); } } else { LOGGER.warn("trustStore [" + fullTrustStore + "] file not exist, please check!"); } tcpClientOptions .setEnabledSecureTransportProtocols(new HashSet<>(Arrays.asList(sslOption.getProtocols().split(",")))); for (String cipher : SSLManager.getEnabledCiphers(sslOption)) { tcpClientOptions.addEnabledCipherSuite(cipher); } if (isFileExists(sslCustom.getFullPath(sslOption.getCrl()))) { tcpClientOptions.addCrlPath(sslCustom.getFullPath(sslOption.getCrl())); } return tcpClientOptions; } private static boolean isFileExists(String name) { if (StringUtils.isEmpty(name)) { return false; } File f = new File(name); if (f.isFile()) { return true; } try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader() == null ? VertxTLSBuilder.class.getClassLoader() : Thread.currentThread().getContextClassLoader(); URL resource = classLoader.getResource(name); return resource != null; } catch (Exception e) { return false; } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.buffer.ByteBuf; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; import io.vertx.core.Future; import io.vertx.core.Verticle; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; import io.vertx.core.impl.SysProps; import io.vertx.core.impl.VertxThread; import io.vertx.core.internal.VertxBootstrap; import io.vertx.core.spi.VertxMetricsFactory; import io.vertx.core.spi.VertxThreadFactory; import io.vertx.core.transport.Transport; /** * VertxUtils * * */ public final class VertxUtils { private static final Logger LOGGER = LoggerFactory.getLogger(VertxUtils.class); private static final long BLOCKED_THREAD_CHECK_INTERVAL = Long.MAX_VALUE / 2; // key为vertx实例名称,以支撑vertx功能分组 private static final Map vertxMap = new ConcurrentHashMapEx<>(); private VertxUtils() { } public static Map getVertxMap() { return vertxMap; } public static void deployVerticle(Vertx vertx, Class cls, int instanceCount) { DeploymentOptions options = new DeploymentOptions().setInstances(instanceCount); vertx.deployVerticle(cls.getName(), options); } public static DeploymentOptions createClientDeployOptions( ClientPoolManager clientMgr, int instanceCount) { DeploymentOptions options = new DeploymentOptions().setInstances(instanceCount); SimpleJsonObject config = new SimpleJsonObject(); config.put(ClientVerticle.CLIENT_MGR, clientMgr); options.setConfig(config); return options; } // deploy Verticle and wait for its success. do not call this method in event-loop thread public static Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) throws InterruptedException { Map result = new HashMap<>(); CountDownLatch latch = new CountDownLatch(1); Future future = vertx.deployVerticle(cls.getName(), options); future.onComplete((success, failure) -> { result.put("code", failure == null); if (failure != null) { result.put("message", failure.getMessage()); LOGGER.error("deploy vertx failed, cause ", failure); } latch.countDown(); }); latch.await(); return result; } public static Vertx getOrCreateVertxByName(String name, VertxOptions vertxOptions, VertxMetricsFactory metricsFactory) { return vertxMap.computeIfAbsent(name, vertxName -> init(name, vertxOptions, metricsFactory)); } public static Vertx init(String name, VertxOptions vertxOptions, VertxMetricsFactory metricsFactory) { if (vertxOptions == null) { vertxOptions = new VertxOptions(); } boolean isDebug = ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp"); if (isDebug) { vertxOptions.setBlockedThreadCheckInterval(BLOCKED_THREAD_CHECK_INTERVAL); LOGGER.info("in debug mode, disable blocked thread check."); } configureVertxFileCaching(vertxOptions); VertxBootstrap bootstrap = bootstrap(vertxOptions, metricsFactory) .threadFactory(new VertxThreadFactory() { @Override public VertxThread newVertxThread(Runnable target, String threadName, boolean worker, long maxExecTime, TimeUnit maxExecTimeUnit) { return VertxThreadFactory.super .newVertxThread(target, name + "-" + threadName, worker, maxExecTime, maxExecTimeUnit); } }); return bootstrap.init().vertx(); } private static VertxBootstrap bootstrap(VertxOptions options, VertxMetricsFactory metricsFactory) { VertxBootstrap bootstrap = VertxBootstrap.create(); bootstrap.options(options); bootstrap.metricsFactory(metricsFactory); Transport tr; if (options.getPreferNativeTransport()) { tr = Transport.nativeTransport(); } else { tr = Transport.NIO; } bootstrap.transport(tr.implementation()); return bootstrap; } /** * 配置vertx的文件缓存功能,默认关闭 */ private static void configureVertxFileCaching(VertxOptions vertxOptions) { boolean disableFileCPResolving = LegacyPropertyFactory .getBooleanProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, true); vertxOptions.getFileSystemOptions().setClassPathResolvingEnabled(!disableFileCPResolving); } // try to reference byte[] // otherwise copy byte[] public static byte[] getBytesFast(InputStream inputStream) throws IOException { if (inputStream instanceof BufferInputStream) { return getBytesFast(((BufferInputStream) inputStream).getByteBuf()); } return IOUtils.toByteArray(inputStream); } public static byte[] getBytesFast(Buffer buffer) { byte[] arr = new byte[buffer.length()]; buffer.getBytes(arr, 0); return arr; } public static byte[] getBytesFast(ByteBuf byteBuf) { if (byteBuf.hasArray()) { return byteBuf.array(); } byte[] arr = new byte[byteBuf.writerIndex()]; byteBuf.getBytes(0, arr); return arr; } public static CompletableFuture closeVertxByName(String name) { LOGGER.info("Closing vertx {}.", name); CompletableFuture future = new CompletableFuture<>(); Vertx vertx = vertxMap.remove(name); if (vertx == null) { LOGGER.info("Vertx {} not exist.", name); future.complete(null); return future; } Future closeFuture = vertx.close(); closeFuture.onComplete((succ, fail) -> { if (fail == null) { LOGGER.info("Success to close vertx {}.", name); future.complete(null); return; } future.completeExceptionally(fail); }); return future; } public static void blockCloseVertxByName(String name) { CompletableFuture future = closeVertxByName(name); try { future.get(30, TimeUnit.SECONDS); } catch (Throwable e) { LOGGER.error("Failed to wait close vertx {}.", name, e); } } public static void blockCloseVertx(Vertx vertx) { CountDownLatch latch = new CountDownLatch(1); Future closeFuture = vertx.close(); closeFuture.onComplete((succ, fail) -> { if (fail == null) { LOGGER.info("Success to close vertx {}.", vertx); } else { LOGGER.info("Failed to close vertx {}.", vertx); } latch.countDown(); }); try { latch.await(30, TimeUnit.SECONDS); } catch (Throwable e) { LOGGER.info("Failed to wait close vertx {}.", vertx); } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client; import io.vertx.core.Context; public interface ClientPoolFactory { CLIENT_POOL createClientPool(Context context); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client; import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import io.vertx.core.Context; import io.vertx.core.Vertx; /** * CLIENT_POOL是一个完备的连接池,支持向同一个目标建立一个或多个连接 * 之所以再包装一层,是因为多个线程使用一个连接池的场景下 * 会导致多个线程抢连接池的同一把锁 * 包装之后,允许使用m个网络线程,每个线程中有1个连接池 * * support both sync and reactive invoke. * 1.sync invoke, bind to a net thread * 2.async but not in eventloop, select but not bind to a net thread * 3.async and in eventloop, use clientPool in self thread * * sync/async is not about net operation, just about consumer invoke mode. */ public class ClientPoolManager { private final Vertx vertx; private final String id = UUID.randomUUID().toString(); private final ClientPoolFactory factory; private final List pools = new CopyOnWriteArrayList<>(); // reactive mode, when call from other thread, must select a context for it // if we use threadId to hash a context, will always select the same context from one thread private final AtomicInteger reactiveNextIndex = new AtomicInteger(); public ClientPoolManager(Vertx vertx, ClientPoolFactory factory) { this.vertx = vertx; this.factory = factory; } public CLIENT_POOL createClientPool(Context context) { CLIENT_POOL pool = factory.createClientPool(context); addPool(context, pool); return pool; } protected void addPool(Context context, CLIENT_POOL pool) { context.put(id, pool); pools.add(pool); } public CLIENT_POOL findClientPool(boolean sync) { return findClientPool(sync, null); } public CLIENT_POOL findClientPool(boolean sync, Context targetContext) { if (sync) { return findThreadBindClientPool(); } // reactive mode return findByContext(targetContext); } protected CLIENT_POOL findByContext() { return findByContext(null); } protected CLIENT_POOL findByContext(Context targetContext) { Context currentContext = targetContext != null ? targetContext : Vertx.currentContext(); if (currentContext != null && currentContext.owner() == vertx && currentContext.isEventLoopContext()) { // standard reactive mode CLIENT_POOL clientPool = currentContext.get(id); if (clientPool != null) { return clientPool; } // Maybe executed in a call back of a reactive call. // The Context is created in a non-event thread and passed to the event loop // thread by vert.x. } // not in correct context: // 1.normal thread // 2.vertx worker thread // 3.other vertx thread // select a existing context assertPoolsInitialized(); int idx = reactiveNextIndex.getAndIncrement() % pools.size(); if (idx < 0) { idx = -idx; } return pools.get(idx); } public CLIENT_POOL findThreadBindClientPool() { assertPoolsInitialized(); int idx = (int) (Thread.currentThread().getId() % pools.size()); return pools.get(idx); } private void assertPoolsInitialized() { if (pools.isEmpty()) { throw new IllegalStateException("client pool not initialized successfully when making calls." + "Please check if system boot up is ready or some errors happened when startup."); } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.AbstractVerticle; public class ClientVerticle extends AbstractVerticle { private static final Logger LOGGER = LoggerFactory.getLogger(ClientVerticle.class); public static final String CLIENT_MGR = "clientMgr"; @SuppressWarnings("unchecked") @Override public void start() throws Exception { try { ClientPoolManager clientMgr = (ClientPoolManager) config().getValue(CLIENT_MGR); clientMgr.createClientPool(context); } catch (Throwable e) { // vert.x got some states that not print error and execute call back in VertexUtils.blockDeploy, we add a log our self. LOGGER.error("", e); throw e; } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.http; import org.apache.servicecomb.foundation.common.encrypt.Encryptions; import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpVersion; import io.vertx.core.http.WebSocketClientOptions; import io.vertx.core.net.ClientOptionsBase; import io.vertx.core.net.ProxyOptions; /** * common Http Client Options must be set by implementations */ public interface HttpClientOptionsSPI { /* unique name in this service */ String clientName(); /* loading order */ int getOrder(); /* can turn off the client */ boolean enabled(); /* config tag is used for group configurations, like ssl, address resolver, etc. set config tag to distinguish * other clients configuration or read the common configuration. */ String getConfigTag(); /***************** vert.x common settings ***************************/ int getEventLoopPoolSize(); boolean useSharedVertx(); /***************** vert.x vertical common settings ***************************/ int getInstanceCount(); boolean isWorker(); String getWorkerPoolName(); int getWorkerPoolSize(); /***************** http common settings ***************************/ HttpVersion getHttpVersion(); int getConnectTimeoutInMillis(); int getIdleTimeoutInSeconds(); boolean isTryUseCompression(); int getMaxWaitQueueSize(); int getMaxPoolSize(); boolean isKeepAlive(); int getMaxHeaderSize(); int getKeepAliveTimeout(); boolean enableLogActivity(); /***************** http 2 settings ****************************/ int getHttp2MultiplexingLimit(); int getHttp2MaxPoolSize(); boolean isUseAlpn(); /***************** proxy settings ***************************/ boolean isProxyEnable(); String getProxyHost(); int getProxyPort(); String getProxyUsername(); String getProxyPassword(); /***************** ssl settings ***************************/ boolean isSsl(); static void buildClientOptionsBase(HttpClientOptionsSPI spi, ClientOptionsBase httpClientOptions) { httpClientOptions.setConnectTimeout(spi.getConnectTimeoutInMillis()); httpClientOptions.setIdleTimeout(spi.getIdleTimeoutInSeconds()); httpClientOptions.setLogActivity(spi.enableLogActivity()); if (spi.isProxyEnable()) { ProxyOptions proxy = new ProxyOptions(); proxy.setHost(spi.getProxyHost()); proxy.setPort(spi.getProxyPort()); proxy.setUsername(spi.getProxyUsername()); proxy.setPassword( Encryptions.decode(spi.getProxyPassword(), spi.getConfigTag())); httpClientOptions.setProxyOptions(proxy); } if (spi.getHttpVersion() == HttpVersion.HTTP_2) { httpClientOptions.setUseAlpn(spi.isUseAlpn()); } } static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { HttpClientOptions httpClientOptions = new HttpClientOptions(); buildClientOptionsBase(spi, httpClientOptions); httpClientOptions.setProtocolVersion(spi.getHttpVersion()); httpClientOptions.setDecompressionSupported(spi.isTryUseCompression()); httpClientOptions.setKeepAlive(spi.isKeepAlive()); httpClientOptions.setMaxHeaderSize(spi.getMaxHeaderSize()); if (spi.getHttpVersion() == HttpVersion.HTTP_2) { httpClientOptions.setHttp2ClearTextUpgrade(false); httpClientOptions.setHttp2MultiplexingLimit(spi.getHttp2MultiplexingLimit()); httpClientOptions.setHttp2KeepAliveTimeout(spi.getKeepAliveTimeout()); } else { httpClientOptions.setKeepAliveTimeout(spi.getKeepAliveTimeout()); } if (spi.isSsl()) { VertxTLSBuilder.buildHttpClientOptions(spi.getConfigTag(), httpClientOptions); } return httpClientOptions; } static WebSocketClientOptions createWebSocketClientOptions(HttpClientOptionsSPI spi, boolean sslEnabled) { WebSocketClientOptions webSocketClientOptions = new WebSocketClientOptions(); buildClientOptionsBase(spi, webSocketClientOptions); if (sslEnabled) { VertxTLSBuilder.buildWebSocketClientOptions(spi.getConfigTag(), webSocketClientOptions); } return webSocketClientOptions; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.http; import org.apache.servicecomb.foundation.vertx.client.ClientPoolFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.PoolOptions; // execute in vertx context public class HttpClientPoolFactory implements ClientPoolFactory { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientPoolFactory.class); private final HttpClientOptions httpClientOptions; private final PoolOptions poolOptions; public HttpClientPoolFactory(HttpClientOptions httpClientOptions, PoolOptions poolOptions) { this.httpClientOptions = httpClientOptions; this.poolOptions = poolOptions; } @Override public HttpClientWithContext createClientPool(Context context) { HttpClient httpClient = context.owner().httpClientBuilder() .with(httpClientOptions) .with(poolOptions) .withConnectHandler(connection -> { LOGGER.debug("http connection connected, local:{}, remote:{}.", connection.localAddress(), connection.remoteAddress()); connection.closeHandler(v -> LOGGER.debug("http connection closed, local:{}, remote:{}.", connection.localAddress(), connection.remoteAddress()) ); connection.exceptionHandler(e -> LOGGER.error("http connection exception, local:{}, remote:{}.", connection.localAddress(), connection.remoteAddress(), e) ); }).build(); return new HttpClientWithContext(httpClient, context); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientWithContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.http; import io.vertx.core.Context; import io.vertx.core.http.HttpClient; public class HttpClientWithContext { public interface RunHandler { void run(HttpClient httpClient); } private final HttpClient httpClient; private final Context context; public HttpClientWithContext(HttpClient httpClient, Context context) { this.httpClient = httpClient; this.context = context; } public HttpClient getHttpClient() { return httpClient; } public void runOnContext(RunHandler handler) { context.runOnContext((v) -> handler.run(httpClient)); } public Context context() { return context; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.http; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.AddressResolverConfig; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.DeploymentOptions; import io.vertx.core.ThreadingModel; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.dns.AddressResolverOptions; import io.vertx.core.http.PoolOptions; import io.vertx.core.http.WebSocketClient; /** * load and manages a set of HttpClient at boot up. */ public class HttpClients { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClients.class); private static final Map> httpClients = new HashMap<>(); /* load at boot up, call this method once and only once. */ public static void load() { List clientOptionsList = SPIServiceUtils.getOrLoadSortedService(HttpClientOptionsSPI.class); clientOptionsList.forEach(option -> { if (option.enabled()) { ClientPoolManager clientPoolManager = httpClients.get(option.clientName()); if (clientPoolManager != null) { LOGGER.warn("client pool {} initialized again.", option.clientName()); } httpClients.put(option.clientName(), createClientPoolManager(option)); } }); } /* destroy at shutdown. */ public static void destroy() { httpClients.clear(); List clientOptionsList = SPIServiceUtils.getOrLoadSortedService(HttpClientOptionsSPI.class); clientOptionsList.forEach(option -> VertxUtils.blockCloseVertxByName(option.clientName())); } private static ClientPoolManager createClientPoolManager(HttpClientOptionsSPI option) { Vertx vertx = getOrCreateVertx(option); PoolOptions poolOptions = new PoolOptions(); poolOptions.setMaxWaitQueueSize(option.getMaxWaitQueueSize()); poolOptions.setHttp1MaxSize(option.getMaxPoolSize()); poolOptions.setHttp2MaxSize(option.getHttp2MaxPoolSize()); ClientPoolManager clientPoolManager = new ClientPoolManager<>(vertx, new HttpClientPoolFactory(HttpClientOptionsSPI.createHttpClientOptions(option), poolOptions)); DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientPoolManager, option.getInstanceCount()) .setThreadingModel(option.isWorker() ? ThreadingModel.WORKER : ThreadingModel.EVENT_LOOP) .setWorkerPoolName(option.getWorkerPoolName()) .setWorkerPoolSize(option.getWorkerPoolSize()); try { VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); return clientPoolManager; } catch (InterruptedException e) { throw new IllegalStateException(e); } } public static WebSocketClient createWebSocketClient(HttpClientOptionsSPI option, boolean sslEnabled) { Vertx vertx = getOrCreateVertx(option); return vertx.createWebSocketClient(HttpClientOptionsSPI.createWebSocketClientOptions(option, sslEnabled)); } private static Vertx getOrCreateVertx(HttpClientOptionsSPI option) { if (option.useSharedVertx()) { return SharedVertxFactory.getSharedVertx(LegacyPropertyFactory.getEnvironment()); } AddressResolverOptions resolverOptions = AddressResolverConfig .getAddressResolverOptions(option.getConfigTag()); VertxOptions vertxOptions = new VertxOptions() .setAddressResolverOptions(resolverOptions) .setEventLoopPoolSize(option.getEventLoopPoolSize()); // Maybe we can deploy only one vert.x for the application. However this has did it like this. return VertxUtils.getOrCreateVertxByName(option.clientName(), vertxOptions, null); } /** * get client instance by name * @param clientName instance name * @return the deployed instance name */ public static HttpClientWithContext getClient(String clientName) { return getClient(clientName, true); } /** * get client instance by name * @param clientName instance name * @param sync reactive or not. false for reactive. * @return the deployed instance name */ public static HttpClientWithContext getClient(String clientName, boolean sync) { return getClient(clientName, sync, null); } /** * get client instance by name * @param clientName instance name * @param sync reactive or not. false for reactive. * @param targetContext running context * @return the deployed instance name */ public static HttpClientWithContext getClient(String clientName, boolean sync, Context targetContext) { ClientPoolManager poolManager = httpClients.get(clientName); if (poolManager == null) { LOGGER.error("client name [{}] not exists, should only happen in tests.", clientName); return null; } return poolManager.findClientPool(sync, targetContext); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import io.vertx.core.Context; public abstract class AbstractTcpClientConnectionPool { // 是在哪个context中创建的 protected Context context; protected NetClientWrapper netClientWrapper; // key为address protected Map tcpClientMap = new ConcurrentHashMapEx<>(); public AbstractTcpClientConnectionPool(Context context, NetClientWrapper netClientWrapper) { this.context = context; this.netClientWrapper = netClientWrapper; startCheckTimeout(context); } protected void startCheckTimeout(Context context) { context.owner().setPeriodic(TimeUnit.SECONDS.toMillis(1), this::onCheckTimeout); } private void onCheckTimeout(Long event) { for (TcpClientConnection client : tcpClientMap.values()) { client.checkTimeout(); } } public T findOrCreateClient(String endpoint) { return tcpClientMap.computeIfAbsent(endpoint, this::create); } protected abstract T create(String endpoint); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import java.util.concurrent.atomic.AtomicLong; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; public abstract class AbstractTcpClientPackage { private static final AtomicLong reqId = new AtomicLong(); public static long getAndIncRequestId() { return reqId.getAndIncrement(); } private volatile long msRequestTimeout; private volatile long finishWriteToBuffer; protected long msgId = getAndIncRequestId(); public long getMsgId() { return msgId; } public long getMsRequestTimeout() { return msRequestTimeout; } public void setMsRequestTimeout(long msRequestTimeout) { this.msRequestTimeout = msRequestTimeout; } public long getFinishWriteToBuffer() { return finishWriteToBuffer; } public void finishWriteToBuffer() { this.finishWriteToBuffer = System.nanoTime(); } public abstract TcpOutputStream createStream(); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPoolFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import org.apache.servicecomb.foundation.vertx.client.ClientPoolFactory; import io.vertx.core.Context; import io.vertx.core.Vertx; public abstract class AbstractTcpClientPoolFactory implements ClientPoolFactory { protected TcpClientConfig normalClientConfig; protected TcpClientConfig sslClientConfig; public AbstractTcpClientPoolFactory(TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { this.normalClientConfig = normalClientConfig; this.sslClientConfig = sslClientConfig; } @Override public CLIENT_POOL createClientPool(Context context) { Vertx vertx = context.owner(); NetClientWrapper netClientWrapper = new NetClientWrapper(vertx, normalClientConfig, sslClientConfig); return doCreateClientPool(context, netClientWrapper); } protected abstract CLIENT_POOL doCreateClientPool(Context context, NetClientWrapper netClientWrapper); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.net.NetClient; import io.vertx.core.net.NetSocket; // netClient do not like httpClient // can not support normal and ssl by the same instance // so we do this wrap public class NetClientWrapper { private final TcpClientConfig normalClientConfig; private final NetClient normalNetClient; private final TcpClientConfig sslClientConfig; private final NetClient sslNetClient; public NetClientWrapper(Vertx vertx, TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { this.normalClientConfig = normalClientConfig; this.normalNetClient = vertx.createNetClient(normalClientConfig); this.sslClientConfig = sslClientConfig; this.sslNetClient = vertx.createNetClient(sslClientConfig); } public TcpClientConfig getClientConfig(boolean ssl) { if (ssl) { return sslClientConfig; } return normalClientConfig; } public Future connect(boolean ssl, int port, String host) { if (ssl) { return sslNetClient.connect(port, host); } return normalNetClient.connect(port, host); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import io.vertx.core.net.NetClientOptions; public class TcpClientConfig extends NetClientOptions { public static final int DEFAULT_LOGIN_TIMEOUT = 30000; private long msLoginTimeout; public TcpClientConfig() { msLoginTimeout = DEFAULT_LOGIN_TIMEOUT; } public long getMsLoginTimeout() { return msLoginTimeout; } public void setMsLoginTimeout(long msLoginTimeout) { this.msLoginTimeout = msLoginTimeout; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeoutException; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.server.TcpParser; import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; import org.apache.servicecomb.foundation.vertx.tcp.TcpConst; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import io.vertx.core.AsyncResult; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; public class TcpClientConnection extends TcpConnection { private static final Logger LOGGER = LoggerFactory.getLogger(TcpClientConnection.class); enum Status { CONNECTING, DISCONNECTED, TRY_LOGIN, WORKING } private final NetClientWrapper netClientWrapper; private final TcpClientConfig clientConfig; private final URIEndpointObject endpoint; private final InetSocketAddress socketAddress; private volatile boolean localSupportLogin = false; private final boolean remoteSupportLogin; private volatile Status status = Status.DISCONNECTED; // save msg before login success. // before login, we can not know parameters, like: zip/codec compatible, and so on // so can only save package, can not save byteBuf private final Queue packageQueue = new ConcurrentLinkedQueue<>(); private volatile Map requestMap = new ConcurrentHashMap<>(); public TcpClientConnection(Context context, NetClientWrapper netClientWrapper, String strEndpoint) { this.setContext(context); this.netClientWrapper = netClientWrapper; endpoint = new URIEndpointObject(strEndpoint); this.socketAddress = endpoint.getSocketAddress(); this.remoteSupportLogin = Boolean.parseBoolean(endpoint.getFirst(TcpConst.LOGIN)); this.clientConfig = netClientWrapper.getClientConfig(endpoint.isSslEnabled()); } public boolean isLocalSupportLogin() { return localSupportLogin; } public TcpClientConfig getClientConfig() { return clientConfig; } public void setLocalSupportLogin(boolean localSupportLogin) { this.localSupportLogin = localSupportLogin; } protected TcpOutputStream createLogin() { return null; } protected boolean onLoginResponse(Buffer bodyBuffer) { return true; } public CompletableFuture send(AbstractTcpClientPackage tcpClientPackage) { CompletableFuture future = new CompletableFuture<>(); send(tcpClientPackage, ar -> { if (ar.failed()) { future.completeExceptionally(ar.cause()); return; } future.complete(ar.result()); }); return future; } public void send(AbstractTcpClientPackage tcpClientPackage, TcpResponseCallback callback) { requestMap.put(tcpClientPackage.getMsgId(), new TcpRequest(tcpClientPackage.getMsRequestTimeout(), callback)); if (writeToBufferQueue(tcpClientPackage)) { return; } // before login success, no optimize, just make sure do not lost data context.runOnContext(v -> { if (!writeToBufferQueue(tcpClientPackage)) { packageQueue.add(tcpClientPackage); } // connect must call in eventloop thread // otherwise vertx will create a new eventloop thread for it if count // of eventloop thread is not up to the limit. if (Status.DISCONNECTED.equals(status)) { connect(); } }); } private boolean writeToBufferQueue(AbstractTcpClientPackage tcpClientPackage) { // read status maybe out of eventloop thread, it's not exact // just optimize for main scenes if (Status.WORKING.equals(status)) { // encode in sender thread try (TcpOutputStream os = tcpClientPackage.createStream()) { write(os.getBuffer()); tcpClientPackage.finishWriteToBuffer(); } return true; } return false; } @Override protected void writeInContext() { writePackageInContext(); super.writeInContext(); } private void writePackageInContext() { for (; ; ) { AbstractTcpClientPackage pkg = packageQueue.poll(); if (pkg == null) { break; } try (TcpOutputStream os = pkg.createStream()) { Buffer buf = os.getBuffer(); netSocket.write(buf); pkg.finishWriteToBuffer(); } } } @VisibleForTesting protected void connect() { this.status = Status.CONNECTING; LOGGER.info("connecting to address {}", socketAddress.toString()); Future result = netClientWrapper.connect(endpoint.isSslEnabled(), socketAddress.getPort(), socketAddress.getHostString()); result.onComplete((socket, fail) -> { if (fail == null) { onConnectSuccess(socket); return; } onConnectFailed(fail); }); } private void onConnectSuccess(NetSocket socket) { LOGGER.info("connected to address {} success in thread {}.", socketAddress.toString(), Thread.currentThread().getName()); // currently, socket always be NetSocketImpl this.initNetSocket((NetSocketImpl) socket); socket.handler(new TcpParser(this::onReply)); socket.exceptionHandler(this::onException); socket.closeHandler(this::onClosed); // 开始登录 tryLogin(); } @VisibleForTesting void onClosed(Void v) { onDisconnected(new IOException("socket closed")); } // 异常断连时,先触发onException,再触发onClosed // 正常断连时,只触发onClosed private void onException(Throwable e) { LOGGER.error("{} disconnected from {}, in thread {}, cause {}", netSocket.localAddress().toString(), socketAddress.toString(), Thread.currentThread().getName(), e.getMessage()); } private void onDisconnected(Throwable e) { this.status = Status.DISCONNECTED; LOGGER.error("{} disconnected from {}, in thread {}, cause {}", netSocket.localAddress().toString(), socketAddress.toString(), Thread.currentThread().getName(), e.getMessage()); clearCachedRequest(e); } protected void tryLogin() { if (!localSupportLogin || !remoteSupportLogin) { LOGGER.error( "local or remote not support login, address={}, localSupportLogin={}, remoteSupportLogin={}.", socketAddress.toString(), localSupportLogin, remoteSupportLogin); onLoginSuccess(); return; } this.status = Status.TRY_LOGIN; LOGGER.info("try login to address {}", socketAddress.toString()); try (TcpOutputStream os = createLogin()) { requestMap.put(os.getMsgId(), new TcpRequest(clientConfig.getMsLoginTimeout(), this::onLoginResponse)); netSocket.write(os.getBuffer()); } } private void onLoginResponse(AsyncResult asyncResult) { if (asyncResult.failed()) { LOGGER.error("login failed, address {}", socketAddress.toString(), asyncResult.cause()); // 在相应回调中设置状态 netSocket.close(); return; } if (!onLoginResponse(asyncResult.result().getBodyBuffer())) { LOGGER.error("login failed, address {}", socketAddress.toString()); // 在相应回调中设置状态 netSocket.close(); return; } LOGGER.info("login success, address {}", socketAddress.toString()); onLoginSuccess(); } private void onLoginSuccess() { this.status = Status.WORKING; writeInContext(); } private void onConnectFailed(Throwable cause) { // 连接失败 this.status = Status.DISCONNECTED; String msg = String.format("connect to address %s failed.", socketAddress.toString()); LOGGER.error(msg, cause); clearCachedRequest(cause); } protected void clearCachedRequest(Throwable cause) { // 在onSendError,用户可能发起一次新的调用,需要避免作多余的清理 Map oldMap = requestMap; requestMap = new ConcurrentHashMap<>(); for (TcpRequest request : oldMap.values()) { request.onSendError(cause); } oldMap.clear(); } protected void onReply(long msgId, Buffer headerBuffer, Buffer bodyBuffer) { TcpRequest request = requestMap.remove(msgId); if (request == null) { LOGGER.error("Unknown reply msgId {}, waiting count {}", msgId, requestMap.size()); return; } request.onReply(headerBuffer, bodyBuffer); } public void checkTimeout() { for (Entry entry : requestMap.entrySet()) { TcpRequest request = entry.getValue(); if (request.isTimeout()) { // 可能正好收到reply,且被处理了,所以这里的remove不一定有效 // 是否有效,根据remove的结果来决定 request = requestMap.remove(entry.getKey()); if (request != null) { String msg = String.format("request timeout, msgId=%d, address=%s", entry.getKey(), socketAddress); LOGGER.error(msg); request.onTimeout(new TimeoutException(msg)); } } } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import io.vertx.core.Context; public class TcpClientConnectionPool extends AbstractTcpClientConnectionPool { public TcpClientConnectionPool(Context context, NetClientWrapper netClientWrapper) { super(context, netClientWrapper); } @Override protected TcpClientConnection create(String endpoint) { return new TcpClientConnection(context, netClientWrapper, endpoint); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; public class TcpClientPackage extends AbstractTcpClientPackage { private final TcpOutputStream os; public TcpClientPackage(TcpOutputStream os) { this.os = os; } @Override public TcpOutputStream createStream() { return os; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPoolFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import io.vertx.core.Context; public class TcpClientPoolFactory extends AbstractTcpClientPoolFactory { public TcpClientPoolFactory(TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { super(normalClientConfig, sslClientConfig); } @Override protected TcpClientConnectionPool doCreateClientPool(Context context, NetClientWrapper netClientWrapper) { return new TcpClientConnectionPool(context, netClientWrapper); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import io.vertx.core.buffer.Buffer; public class TcpData { private Buffer headerBuffer; private Buffer bodyBuffer; public TcpData(Buffer headerBuffer, Buffer bodyBuffer) { this.headerBuffer = headerBuffer; this.bodyBuffer = bodyBuffer; } public Buffer getHeaderBuffer() { return headerBuffer; } public void setHeaderBuffer(Buffer headerBuffer) { this.headerBuffer = headerBuffer; } public Buffer getBodyBuffer() { return bodyBuffer; } public void setBodyBuffer(Buffer bodyBuffer) { this.bodyBuffer = bodyBuffer; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import java.util.concurrent.TimeoutException; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; public class TcpRequest { private final long begin; private final long msTimeout; private final Context callContext; private final long threadId; private final TcpResponseCallback responseCallback; public TcpRequest(long msTimeout, TcpResponseCallback responseCallback) { callContext = Vertx.currentContext(); threadId = Thread.currentThread().getId(); this.begin = System.currentTimeMillis(); this.msTimeout = msTimeout; this.responseCallback = responseCallback; } public void onReply(Buffer headerBuffer, Buffer bodyBuffer) { TcpData tcpData = new TcpData(headerBuffer, bodyBuffer); if (callContext == null || threadId == Thread.currentThread().getId()) { responseCallback.success(tcpData); return; } callContext.runOnContext(Void -> responseCallback.success(tcpData)); } public void onSendError(Throwable e) { responseCallback.fail(e); } public boolean isTimeout() { return System.currentTimeMillis() - begin >= msTimeout; } public void onTimeout(TimeoutException e) { responseCallback.fail(e); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpResponseCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import org.apache.servicecomb.foundation.vertx.AsyncResultCallback; public interface TcpResponseCallback extends AsyncResultCallback { } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/SinglePoolBlockingExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.executor; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SinglePoolBlockingExecutor implements Executor { private static final Logger LOGGER = LoggerFactory.getLogger(SinglePoolBlockingExecutor.class); private static final Executor SINGLE_POOL = Executors.newSingleThreadExecutor((r) -> { Thread thread = new Thread(r); thread.setName("single-pool-blocking-executor"); return thread; }); public static SinglePoolBlockingExecutor create() { return new SinglePoolBlockingExecutor(); } private SinglePoolBlockingExecutor() { } @Override public void execute(Runnable command) { SINGLE_POOL.execute(() -> { try { command.run(); } catch (Throwable e) { LOGGER.error("Logic should not throw exception, please fix it", e); } }); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxContextExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.executor; import java.util.concurrent.Executor; import io.vertx.core.Context; import io.vertx.core.Vertx; public class VertxContextExecutor implements Executor { public static VertxContextExecutor create(Vertx vertx) { return new VertxContextExecutor(vertx.getOrCreateContext()); } public static VertxContextExecutor create(Context context) { return new VertxContextExecutor(context); } private final Context context; private VertxContextExecutor(Context context) { this.context = context; } @Override public void execute(Runnable command) { if (context == Vertx.currentContext()) { command.run(); return; } context.runOnContext(v -> command.run()); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.executor; import java.util.concurrent.Executor; import io.vertx.core.Vertx; public class VertxWorkerExecutor implements Executor { @Override public void execute(Runnable command) { Vertx.currentContext().owner().executeBlocking(() -> { command.run(); return null; }, false); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletConnection; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpUpgradeHandler; import jakarta.servlet.http.Part; public abstract class AbstractHttpServletRequest extends BodyBufferSupportImpl implements HttpServletRequestEx { private final Map attributeMap = new HashMap<>(); @Override public Object getAttribute(String name) { return attributeMap.get(name); } @Override public Enumeration getAttributeNames() { return Collections.enumeration(attributeMap.keySet()); } @Override public String getCharacterEncoding() { throw new Error("not supported method"); } @Override public void setCharacterEncoding(String env) throws UnsupportedEncodingException { throw new Error("not supported method"); } @Override public String getRequestId() { throw new Error("not supported method"); } @Override public String getProtocolRequestId() { throw new Error("not supported method"); } @Override public ServletConnection getServletConnection() { throw new Error("not supported method"); } @Override public int getContentLength() { throw new Error("not supported method"); } @Override public long getContentLengthLong() { throw new Error("not supported method"); } @Override public String getContentType() { throw new Error("not supported method"); } @Override public ServletInputStream getInputStream() throws IOException { throw new Error("not supported method"); } @Override public String getParameter(String name) { throw new Error("not supported method"); } @Override public Enumeration getParameterNames() { throw new Error("not supported method"); } @Override public String[] getParameterValues(String name) { throw new Error("not supported method"); } @Override public Map getParameterMap() { throw new Error("not supported method"); } @Override public String getProtocol() { throw new Error("not supported method"); } @Override public String getScheme() { throw new Error("not supported method"); } @Override public String getServerName() { throw new Error("not supported method"); } @Override public int getServerPort() { throw new Error("not supported method"); } @Override public BufferedReader getReader() throws IOException { throw new Error("not supported method"); } @Override public String getRemoteAddr() { throw new Error("not supported method"); } @Override public String getRemoteHost() { throw new Error("not supported method"); } @Override public void setAttribute(String name, Object o) { attributeMap.put(name, o); } @Override public void removeAttribute(String name) { attributeMap.remove(name); } @Override public Locale getLocale() { throw new Error("not supported method"); } @Override public Enumeration getLocales() { throw new Error("not supported method"); } @Override public boolean isSecure() { throw new Error("not supported method"); } @Override public RequestDispatcher getRequestDispatcher(String path) { throw new Error("not supported method"); } @Override public int getRemotePort() { throw new Error("not supported method"); } @Override public String getLocalName() { throw new Error("not supported method"); } @Override public String getLocalAddr() { throw new Error("not supported method"); } @Override public int getLocalPort() { throw new Error("not supported method"); } @Override public ServletContext getServletContext() { throw new Error("not supported method"); } @Override public AsyncContext startAsync() throws IllegalStateException { throw new Error("not supported method"); } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { throw new Error("not supported method"); } @Override public boolean isAsyncStarted() { throw new Error("not supported method"); } @Override public boolean isAsyncSupported() { throw new Error("not supported method"); } @Override public AsyncContext getAsyncContext() { throw new Error("not supported method"); } @Override public DispatcherType getDispatcherType() { throw new Error("not supported method"); } @Override public String getAuthType() { throw new Error("not supported method"); } @Override public Cookie[] getCookies() { throw new Error("not supported method"); } @Override public long getDateHeader(String name) { throw new Error("not supported method"); } @Override public String getHeader(String name) { throw new Error("not supported method"); } @Override public Enumeration getHeaders(String name) { throw new Error("not supported method"); } @Override public Enumeration getHeaderNames() { throw new Error("not supported method"); } @Override public int getIntHeader(String name) { throw new Error("not supported method"); } @Override public String getMethod() { throw new Error("not supported method"); } @Override public String getPathInfo() { throw new Error("not supported method"); } @Override public String getPathTranslated() { throw new Error("not supported method"); } @Override public String getContextPath() { throw new Error("not supported method"); } @Override public String getQueryString() { throw new Error("not supported method"); } @Override public String getRemoteUser() { throw new Error("not supported method"); } @Override public boolean isUserInRole(String role) { throw new Error("not supported method"); } @Override public Principal getUserPrincipal() { throw new Error("not supported method"); } @Override public String getRequestedSessionId() { throw new Error("not supported method"); } @Override public String getRequestURI() { throw new Error("not supported method"); } @Override public StringBuffer getRequestURL() { throw new Error("not supported method"); } @Override public String getServletPath() { throw new Error("not supported method"); } @Override public HttpSession getSession(boolean create) { throw new Error("not supported method"); } @Override public HttpSession getSession() { throw new Error("not supported method"); } @Override public String changeSessionId() { throw new Error("not supported method"); } @Override public boolean isRequestedSessionIdValid() { throw new Error("not supported method"); } @Override public boolean isRequestedSessionIdFromCookie() { throw new Error("not supported method"); } @Override public boolean isRequestedSessionIdFromURL() { throw new Error("not supported method"); } @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { throw new Error("not supported method"); } @Override public void login(String username, String password) throws ServletException { throw new Error("not supported method"); } @Override public void logout() throws ServletException { throw new Error("not supported method"); } @Override public Collection getParts() throws IOException, ServletException { throw new Error("not supported method"); } @Override public Part getPart(String name) throws IOException, ServletException { throw new Error("not supported method"); } @Override public T upgrade(Class handlerClass) throws IOException, ServletException { throw new Error("not supported method"); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; import io.vertx.core.buffer.Buffer; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.Response.StatusType; public abstract class AbstractHttpServletResponse extends BodyBufferSupportImpl implements HttpServletResponseEx { private final Map attributes = new HashMap<>(); @Override public String getCharacterEncoding() { throw new Error("not supported method"); } @Override public String getContentType() { throw new Error("not supported method"); } @Override public ServletOutputStream getOutputStream() throws IOException { throw new Error("not supported method"); } @Override public PrintWriter getWriter() throws IOException { throw new Error("not supported method"); } @Override public void setCharacterEncoding(String charset) { throw new Error("not supported method"); } @Override public void setContentLength(int len) { throw new Error("not supported method"); } @Override public void setContentLengthLong(long len) { throw new Error("not supported method"); } @Override public void setContentType(String type) { throw new Error("not supported method"); } @Override public void setBufferSize(int size) { throw new Error("not supported method"); } @Override public int getBufferSize() { throw new Error("not supported method"); } @Override public void flushBuffer() throws IOException { // for vert.x do noting } @Override public void endResponse() throws IOException { throw new Error("not supported method"); } @Override public void resetBuffer() { throw new Error("not supported method"); } @Override public boolean isCommitted() { throw new Error("not supported method"); } @Override public void reset() { throw new Error("not supported method"); } @Override public void setLocale(Locale loc) { throw new Error("not supported method"); } @Override public Locale getLocale() { throw new Error("not supported method"); } @Override public void addCookie(Cookie cookie) { throw new Error("not supported method"); } @Override public boolean containsHeader(String name) { throw new Error("not supported method"); } @Override public String encodeURL(String url) { throw new Error("not supported method"); } @Override public String encodeRedirectURL(String url) { throw new Error("not supported method"); } @Override public void sendError(int sc, String msg) throws IOException { throw new Error("not supported method"); } @Override public void sendError(int sc) throws IOException { throw new Error("not supported method"); } @Override public void sendRedirect(String location) throws IOException { throw new Error("not supported method"); } @Override public void setDateHeader(String name, long date) { throw new Error("not supported method"); } @Override public void addDateHeader(String name, long date) { throw new Error("not supported method"); } @Override public void setHeader(String name, String value) { throw new Error("not supported method"); } @Override public void addHeader(String name, String value) { throw new Error("not supported method"); } @Override public void setIntHeader(String name, int value) { throw new Error("not supported method"); } @Override public void addIntHeader(String name, int value) { throw new Error("not supported method"); } @Override public void setStatus(int sc) { throw new Error("not supported method"); } @Override public int getStatus() { throw new Error("not supported method"); } @Override public String getHeader(String name) { throw new Error("not supported method"); } @Override public Collection getHeaders(String name) { throw new Error("not supported method"); } @Override public Collection getHeaderNames() { throw new Error("not supported method"); } @Override public StatusType getStatusType() { throw new Error("not supported method"); } @Override public void setAttribute(String key, Object value) { this.attributes.put(key, value); } @Override public Object getAttribute(String key) { return this.attributes.get(key); } @Override public CompletableFuture sendPart(Part body) { throw new Error("not supported method"); } @Override public CompletableFuture sendBuffer(Buffer buffer) { throw new Error("not supported method"); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import io.vertx.core.buffer.Buffer; public interface BodyBufferSupport { void setBodyBuffer(Buffer bodyBuffer); Buffer getBodyBuffer(); // notice: byte[] maybe null, and length maybe bigger than body length byte[] getBodyBytes(); int getBodyBytesLength(); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupportImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.foundation.vertx.VertxUtils; import io.vertx.core.buffer.Buffer; public class BodyBufferSupportImpl implements BodyBufferSupport { protected Buffer bodyBuffer; private byte[] bodyBytes; private int bodyLength; @Override public void setBodyBuffer(Buffer bodyBuffer) { this.bodyBuffer = bodyBuffer; this.bodyBytes = null; this.bodyLength = 0; } private void prepare() { if (bodyBytes == null && bodyBuffer != null) { bodyLength = bodyBuffer.length(); bodyBytes = VertxUtils.getBytesFast(bodyBuffer); } } @Override public Buffer getBodyBuffer() { return bodyBuffer; } @VisibleForTesting void setBodyBytes(byte[] bodyBytes) { this.bodyBytes = bodyBytes; } @Override public byte[] getBodyBytes() { prepare(); return bodyBytes; } @VisibleForTesting void setBodyLength(int bodyLength) { this.bodyLength = bodyLength; } @Override public int getBodyBytesLength() { prepare(); return bodyLength; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/DownloadUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.common.part.FilePartForSend; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.http.HttpHeaders; /** * internal api */ public final class DownloadUtils { private static final Logger LOGGER = LoggerFactory.getLogger(DownloadUtils.class); private DownloadUtils() { } public static void prepareDownloadHeader(HttpServletResponseEx responseEx, Part part) { if (responseEx.getHeader(HttpHeaders.CONTENT_LENGTH.toString()) == null) { responseEx.setChunked(true); } if (part == null) { return; } if (responseEx.getHeader(HttpHeaders.CONTENT_TYPE.toString()) == null) { if (responseEx.getContentType() != null) { responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), responseEx.getContentType()); } else { responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), part.getContentType()); } } if (responseEx.getHeader(jakarta.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION) == null) { // to support chinese and space filename in firefox // must use "filename*", (https://tools.ietf.org/html/rtf6266) String encodedFileName = HttpUtils.uriEncodePath(part.getSubmittedFileName()); responseEx.setHeader(jakarta.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + encodedFileName + ";filename*=utf-8''" + encodedFileName); } } public static void clearPartResource(Part part) { if (part == null) { return; } if (part instanceof FilePartForSend && ((FilePartForSend) part).isDeleteAfterFinished()) { try { part.delete(); } catch (IOException e) { LOGGER.error("Failed to delete temp file: {}.", ((FilePartForSend) part).getAbsolutePath(), e); } } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/EmptyAsyncContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncListener; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; public class EmptyAsyncContext implements AsyncContext { @Override public ServletRequest getRequest() { return null; } @Override public ServletResponse getResponse() { return null; } @Override public boolean hasOriginalRequestAndResponse() { return false; } @Override public void dispatch() { } @Override public void dispatch(String path) { } @Override public void dispatch(ServletContext context, String path) { } @Override public void complete() { } @Override public void start(Runnable run) { } @Override public void addListener(AsyncListener listener) { } @Override public void addListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) { } @Override public T createListener(Class clazz) throws ServletException { return null; } @Override public void setTimeout(long timeout) { } @Override public long getTimeout() { return 0; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.common.part.AbstractPart; import io.vertx.ext.web.FileUpload; public class FileUploadPart extends AbstractPart { private final FileUpload fileUpload; public FileUploadPart(FileUpload fileUpload) { this.fileUpload = fileUpload; } @Override public InputStream getInputStream() throws IOException { return Files.newInputStream(new File(fileUpload.uploadedFileName()).toPath()); } @Override public String getContentType() { return fileUpload.contentType(); } @Override public String getName() { return fileUpload.name(); } @Override public String getSubmittedFileName() { return fileUpload.fileName(); } @Override public long getSize() { return fileUpload.size(); } @Override public void write(String fileName) throws IOException { FileUtils.copyFile(new File(fileUpload.uploadedFileName()), new File(fileName)); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletRequestEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import jakarta.servlet.http.HttpServletRequest; public interface HttpServletRequestEx extends HttpServletRequest, BodyBufferSupport { default void setHeader(String name, String value) { } default void addHeader(String name, String value) { } default void setParameter(String name, String value) { } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.CompletableFuture; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpHeaders; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.Response.StatusType; public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSupport { StatusType getStatusType(); void setAttribute(String key, Object value); Object getAttribute(String key); CompletableFuture sendPart(Part body); CompletableFuture sendBuffer(Buffer buffer); default void setChunked(boolean chunked) { setHeader(HttpHeaders.TRANSFER_ENCODING.toString(), HttpHeaders.CHUNKED.toString()); } void endResponse() throws IOException; @Override default ServletOutputStream getOutputStream() throws IOException { throw new IOException("Not allowed"); } @Override default PrintWriter getWriter() throws IOException { throw new IOException("Not allowed"); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.File; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.common.part.AbstractPart; import org.apache.servicecomb.foundation.vertx.stream.PumpCommon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.AsyncFile; import io.vertx.core.file.OpenOptions; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.WriteStream; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; /** * this is not a really part type, all method extend from AbstractPart is undefined except:
* 1.getContentType
* 2.getSubmittedFileName
* extend from AbstractPart just because want to make it be Part type, * so that can be sent by * {@link org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse#sendPart(Part) VertxServerResponseToHttpServletResponse.sendPart} */ public class ReadStreamPart extends AbstractPart { private static final Logger LOGGER = LoggerFactory.getLogger(ReadStreamPart.class); private final Context context; private final ReadStream readStream; public ReadStreamPart(Context context, HttpClientResponse httpClientResponse) { this(context, (ReadStream) httpClientResponse); setSubmittedFileName( HttpUtils.parseFileNameFromHeaderValue(httpClientResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION))); String contentType = httpClientResponse.getHeader(HttpHeaders.CONTENT_TYPE); if (StringUtils.isNotEmpty(contentType)) { this.contentType(contentType); } } public ReadStreamPart(Context context, ReadStream readStream) { this.context = context; this.readStream = readStream; readStream.pause(); } public Context getContext() { return context; } public ReadStream getReadStream() { return readStream; } /** * @return future of save action
* * important: WriteStream did not provide endHandler, so we can not know when will really finished write. * so the return future only means finished read from readStream. */ public CompletableFuture saveToWriteStream(WriteStream writeStream) { return new PumpCommon().pump(readStream, writeStream, null); } public CompletableFuture saveAsBytes() { return saveAs(Buffer::getBytes); } public CompletableFuture saveAsString() { return saveAs(Buffer::toString); } public CompletableFuture saveAs(Function converter) { CompletableFuture future = new CompletableFuture<>(); Buffer buffer = Buffer.buffer(); // if readStream.resume() not run on correct eventloop, will: // 1.create a context task to save last chunk data to buffer // 2.activate connection to read new data // but maybe 2 will run before 1, that will cause lost data or get incorrect data context.runOnContext(V -> { readStream.exceptionHandler(future::completeExceptionally); readStream.handler(buffer::appendBuffer); readStream.endHandler(v -> future.complete(converter.apply(buffer))); readStream.resume(); }); return future; } /** * @return future of save to file, future complete means write to file finished */ public CompletableFuture saveToFile(String fileName) { File file = new File(fileName); file.getParentFile().mkdirs(); OpenOptions openOptions = new OpenOptions().setCreateNew(true); return saveToFile(file, openOptions); } /** * @return future of save to file, future complete means write to file finished */ public CompletableFuture saveToFile(File file, OpenOptions openOptions) { CompletableFuture future = new CompletableFuture<>(); context.runOnContext((v) -> { Vertx vertx = context.owner(); Future openFuture = vertx.fileSystem().open(file.getAbsolutePath(), openOptions); openFuture.onComplete((s, f) -> onFileOpened(file, s, f, future)); }); return future; } protected void onFileOpened(File file, AsyncFile asyncFile, Throwable failure, CompletableFuture future) { if (failure != null) { future.completeExceptionally(failure); return; } CompletableFuture saveFuture = saveToWriteStream(asyncFile); saveFuture.whenComplete((v, saveException) -> { Future result = asyncFile.close(); result.onComplete((s, f) -> { if (f != null) { LOGGER.error("Failed to close file {}.", file); } // whatever close success or failed // will not affect to result // result just only related to write if (saveException == null) { future.complete(file); return; } future.completeExceptionally(saveException); }); }); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import com.google.common.annotations.VisibleForTesting; import io.vertx.core.buffer.Buffer; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.core.MediaType; public class StandardHttpServletRequestEx extends HttpServletRequestWrapper implements HttpServletRequestEx { private final BodyBufferSupport bodyBuffer = new BodyBufferSupportImpl(); private boolean cacheRequest; private ServletInputStream inputStream; // by servlet specification // only parse application/x-www-form-urlencoded of post request automatically // we will parse this even not post method private Map parameterMap; public StandardHttpServletRequestEx(HttpServletRequest request) { super(request); } public void setCacheRequest(boolean cacheRequest) { this.cacheRequest = cacheRequest; } @VisibleForTesting public boolean isCacheRequest() { return cacheRequest; } @Override public ServletInputStream getInputStream() throws IOException { if (this.inputStream == null) { if (cacheRequest) { byte[] inputBytes = IOUtils.toByteArray(getRequest().getInputStream()); Buffer byteBuf = Buffer.buffer(inputBytes); this.inputStream = new BufferInputStream(byteBuf); setBodyBuffer(byteBuf); } else { this.inputStream = getRequest().getInputStream(); } } return this.inputStream; } @Override public void setBodyBuffer(Buffer bodyBuffer) { this.bodyBuffer.setBodyBuffer(bodyBuffer); } @Override public Buffer getBodyBuffer() { return bodyBuffer.getBodyBuffer(); } @Override public byte[] getBodyBytes() { return bodyBuffer.getBodyBytes(); } @Override public int getBodyBytesLength() { return bodyBuffer.getBodyBytesLength(); } private Map parseParameterMap() { // 1.post method already parsed by servlet // 2.not APPLICATION_FORM_URLENCODED, no need to enhance if (getMethod().equalsIgnoreCase(HttpMethod.POST) || !StringUtils.startsWithIgnoreCase(getContentType(), MediaType.APPLICATION_FORM_URLENCODED)) { return super.getParameterMap(); } Map> listMap = parseUrlEncodedBody(); mergeParameterMaptoListMap(listMap); return convertListMapToArrayMap(listMap); } private Map convertListMapToArrayMap(Map> listMap) { Map arrayMap = new HashMap<>(); for (Entry> entry : listMap.entrySet()) { arrayMap.put(entry.getKey(), entry.getValue().toArray(new String[0])); } return arrayMap; } private void mergeParameterMaptoListMap(Map> listMap) { for (Entry entry : super.getParameterMap().entrySet()) { List values = listMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()); // follow servlet behavior, inherited value first, and then body value values.addAll(0, Arrays.asList(entry.getValue())); } } private Map> parseUrlEncodedBody() { try (InputStream inputStream = getInputStream()) { Map> listMap = new HashMap<>(); String body = IOUtils.toString(inputStream, StandardCharsets.UTF_8); List pairs = URLEncodedUtils .parse(body, getCharacterEncoding() == null ? null : Charset.forName(getCharacterEncoding())); for (NameValuePair pair : pairs) { List values = listMap.computeIfAbsent(pair.getName(), k -> new ArrayList<>()); values.add(pair.getValue()); } return listMap; } catch (IOException e) { throw new IllegalStateException("", e); } } @Override public String[] getParameterValues(String name) { return getParameterMap().get(name); } @Override public String getParameter(String name) { String[] values = getParameterMap().get(name); return values == null ? null : values[0]; } @Override public Enumeration getParameterNames() { return Collections.enumeration(getParameterMap().keySet()); } @Override public Map getParameterMap() { if (parameterMap == null) { parameterMap = parseParameterMap(); } return parameterMap; } @Override public void setParameter(String name, String value) { getParameterMap().put(name, new String[] {value}); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponseWrapper; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; public class StandardHttpServletResponseEx extends HttpServletResponseWrapper implements HttpServletResponseEx { private final BodyBufferSupport bodyBuffer = new BodyBufferSupportImpl(); private final Map attributes = new HashMap<>(); private StatusType statusType; public StandardHttpServletResponseEx(HttpServletResponse response) { super(response); } @Override public void setBodyBuffer(Buffer bodyBuffer) { this.bodyBuffer.setBodyBuffer(bodyBuffer); } @Override public Buffer getBodyBuffer() { return bodyBuffer.getBodyBuffer(); } @Override public byte[] getBodyBytes() { return bodyBuffer.getBodyBytes(); } @Override public int getBodyBytesLength() { return bodyBuffer.getBodyBytesLength(); } @Override public void setStatus(int sc) { super.setStatus(sc); statusType = Status.fromStatusCode(sc); } @Override public int getStatus() { return statusType.getStatusCode(); } @Override public StatusType getStatusType() { return statusType; } @Override public void endResponse() throws IOException { super.flushBuffer(); } @Override public void setAttribute(String key, Object value) { this.attributes.put(key, value); } @Override public Object getAttribute(String key) { return this.attributes.get(key); } @Override public CompletableFuture sendPart(Part part) { if (part == null) { return CompletableFuture.completedFuture(null); } DownloadUtils.prepareDownloadHeader(this, part); OutputStream outputStream; try { outputStream = getOutputStream(); } catch (IOException e) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(e); return future; } // if context is null, then will switch to sync logic Context context = Vertx.currentContext(); return new PumpFromPart(context, part).toOutputStream(outputStream, false); } @Override public CompletableFuture sendBuffer(Buffer buffer) { CompletableFuture future = new CompletableFuture<>(); try { getOutputStream().write(buffer.getBytes(), 0, buffer.length()); future.complete(null); } catch (IOException e) { future.completeExceptionally(e); } return future; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientRequestToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.util.Collections; import java.util.Enumeration; import org.apache.servicecomb.foundation.common.http.HttpUtils; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import jakarta.ws.rs.core.HttpHeaders; public class VertxClientRequestToHttpServletRequest extends AbstractHttpServletRequest { private final HttpClientRequest clientRequest; private String characterEncoding; public VertxClientRequestToHttpServletRequest(HttpClientRequest clientRequest, Buffer bodyBuffer) { this.clientRequest = clientRequest; setBodyBuffer(bodyBuffer); } @Override public String getRequestURI() { return clientRequest.path(); } @Override public StringBuffer getRequestURL() { return new StringBuffer(clientRequest.path()); } @Override public String getQueryString() { return clientRequest.query(); } @Override public String getHeader(String name) { return clientRequest.headers().get(name); } @Override public Enumeration getHeaders(String name) { return Collections.enumeration(clientRequest.headers().getAll(name)); } @Override public Enumeration getHeaderNames() { return Collections.enumeration(clientRequest.headers().names()); } @Override public void setHeader(String name, String value) { clientRequest.headers().set(name, value); } @Override public void addHeader(String name, String value) { clientRequest.headers().add(name, value); } @Override public String getContextPath() { return ""; } @Override public String getMethod() { return clientRequest.getMethod().name(); } @Override public String getContentType() { return clientRequest.headers().get(HttpHeaders.CONTENT_TYPE); } @Override public String getCharacterEncoding() { if (characterEncoding == null) { characterEncoding = HttpUtils.getCharsetFromContentType(getContentType()); } return characterEncoding; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientResponseToHttpServletResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.util.Collection; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.foundation.common.http.HttpStatus; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientResponse; public class VertxClientResponseToHttpServletResponse extends AbstractHttpServletResponse { private final HttpClientResponse clientResponse; private StatusType statusType; public VertxClientResponseToHttpServletResponse(HttpClientResponse clientResponse, Buffer bodyBuffer) { this.clientResponse = clientResponse; setBodyBuffer(bodyBuffer); } @Override public int getStatus() { return clientResponse.statusCode(); } @Override public StatusType getStatusType() { if (statusType == null) { statusType = new HttpStatus(clientResponse.statusCode(), clientResponse.statusMessage()); } return statusType; } @Override public String getContentType() { return clientResponse.getHeader(HttpHeaders.CONTENT_TYPE); } @Override public String getHeader(String name) { return clientResponse.getHeader(name); } @Override public Collection getHeaders(String name) { return clientResponse.headers().getAll(name); } @Override public Collection getHeaderNames() { return clientResponse.headers().names(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.FileUpload; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.impl.RoutingContextInternal; import jakarta.servlet.AsyncContext; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; // wrap vertx http request to Servlet http request public class VertxServerRequestToHttpServletRequest extends AbstractHttpServletRequest { private static final Logger LOGGER = LoggerFactory.getLogger(VertxServerRequestToHttpServletRequest.class); private static final EmptyAsyncContext EMPTY_ASYNC_CONTEXT = new EmptyAsyncContext(); private final RoutingContext context; private final HttpServerRequest vertxRequest; private Cookie[] cookies; private ServletInputStream inputStream; private String path; private final SocketAddress socketAddress; // cache from convert vertx parameters to servlet parameters private Map parameterMap; private String characterEncoding; public VertxServerRequestToHttpServletRequest(RoutingContext context, String path) { this(context); this.path = path; } public VertxServerRequestToHttpServletRequest(RoutingContext context) { this.context = context; this.vertxRequest = context.request(); this.socketAddress = this.vertxRequest.remoteAddress(); super.setBodyBuffer(context.body().buffer()); } @Override public void setBodyBuffer(Buffer bodyBuffer) { super.setBodyBuffer(bodyBuffer); if (context instanceof RoutingContextInternal) { RoutingContextInternal contextInternal = (RoutingContextInternal) context; contextInternal.setBody(bodyBuffer); } this.inputStream = null; } @Override public String getContentType() { return this.vertxRequest.getHeader(HttpHeaders.CONTENT_TYPE); } @Override public Cookie[] getCookies() { if (cookies == null) { Set cookieSet = context.request().cookies(); Cookie[] tmpCookies = new Cookie[cookieSet.size()]; int idx = 0; for (io.vertx.core.http.Cookie vertxCookie : cookieSet) { Cookie cookie = new Cookie(vertxCookie.getName(), vertxCookie.getValue()); tmpCookies[idx] = cookie; idx++; } cookies = tmpCookies; } return cookies; } @Override public String getParameter(String name) { if (parameterMap != null) { String[] values = parameterMap.get(name); return values == null ? null : values[0]; } return this.vertxRequest.getParam(name); } @Override public Enumeration getParameterNames() { if (parameterMap != null) { return Collections.enumeration(parameterMap.keySet()); } return Collections.enumeration(this.vertxRequest.params().names()); } @Override public String[] getParameterValues(String name) { if (parameterMap != null) { return parameterMap.get(name); } List paramList = this.vertxRequest.params().getAll(name); return paramList.toArray(new String[0]); } @Override public Map getParameterMap() { if (parameterMap == null) { Map paramMap = new HashMap<>(); MultiMap map = this.vertxRequest.params(); for (String name : map.names()) { paramMap.put(name, map.getAll(name).toArray(new String[0])); } parameterMap = paramMap; } return parameterMap; } @Override public void setParameter(String name, String value) { if (parameterMap != null) { parameterMap.put(name, new String[] {value}); return; } vertxRequest.params().set(name, value); } @Override public String getScheme() { return this.vertxRequest.scheme(); } @Override public String getRemoteAddr() { return this.socketAddress.host(); } @Override public String getRemoteHost() { return this.socketAddress.host(); } @Override public int getRemotePort() { return this.socketAddress.port(); } @Override public String getLocalAddr() { return this.vertxRequest.localAddress().host(); } @Override public int getLocalPort() { return this.vertxRequest.localAddress().port(); } @Override public String getHeader(String name) { return this.vertxRequest.getHeader(name); } @Override public Enumeration getHeaders(String name) { return Collections.enumeration(this.vertxRequest.headers().getAll(name)); } @Override public Enumeration getHeaderNames() { return Collections.enumeration(vertxRequest.headers().names()); } @Override public int getIntHeader(String name) { String header = this.vertxRequest.getHeader(name); if (header == null) { return -1; } return Integer.parseInt(header); } @Override public String getMethod() { return this.vertxRequest.method().name(); } @Override public String getPathInfo() { return this.vertxRequest.path(); } @Override public String getQueryString() { return this.vertxRequest.query(); } @Override public String getRequestURI() { if (this.path == null) { this.path = vertxRequest.path(); } return this.path; } @Override public StringBuffer getRequestURL() { if (this.path == null) { this.path = vertxRequest.path(); } return new StringBuffer(this.path); } @Override public String getServletPath() { return this.getPathInfo(); } @Override public String getContextPath() { return ""; } @Override public ServletInputStream getInputStream() { if (inputStream != null) { return inputStream; } if (context.body().buffer() == null) { return null; } inputStream = new BufferInputStream(context.body().buffer()); return inputStream; } @Override public AsyncContext getAsyncContext() { return EMPTY_ASYNC_CONTEXT; } @Override public Part getPart(String name) { Optional upload = context.fileUploads() .stream() .filter(fileUpload -> fileUpload.name().equals(name)) .findFirst(); if (!upload.isPresent()) { LOGGER.debug("No such file with name: {}.", name); return null; } final FileUpload fileUpload = upload.get(); return new FileUploadPart(fileUpload); } @Override public Collection getParts() { return context.fileUploads().stream().map(FileUploadPart::new).collect(Collectors.toList()); } public RoutingContext getContext() { return context; } @Override public String getCharacterEncoding() { if (characterEncoding == null) { characterEncoding = HttpUtils.getCharsetFromContentType(getContentType()); } return characterEncoding; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.util.Collection; import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.foundation.common.http.HttpStatus; import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerResponse; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response.StatusType; public class VertxServerResponseToHttpServletResponse extends AbstractHttpServletResponse { private final Context context; private final HttpServerResponse serverResponse; private StatusType statusType; public VertxServerResponseToHttpServletResponse(HttpServerResponse serverResponse) { this.context = Vertx.currentContext(); this.serverResponse = serverResponse; Objects.requireNonNull(context, "must run in vertx context."); } @Override public void setContentType(String type) { serverResponse.headers().set(HttpHeaders.CONTENT_TYPE, type); } @Override public void setContentLength(int len) { serverResponse.headers().set(HttpHeaders.CONTENT_LENGTH, String.valueOf(len)); } @Override public void setStatus(int sc) { serverResponse.setStatusCode(sc); } @Override public StatusType getStatusType() { if (statusType == null) { statusType = new HttpStatus(serverResponse.getStatusCode(), serverResponse.getStatusMessage()); } return statusType; } @Override public void addHeader(String name, String value) { serverResponse.headers().add(name, value); } @Override public void setHeader(String name, String value) { serverResponse.headers().set(name, value); } @Override public int getStatus() { return serverResponse.getStatusCode(); } @Override public String getContentType() { return serverResponse.headers().get(HttpHeaders.CONTENT_TYPE); } @Override public String getHeader(String name) { return serverResponse.headers().get(name); } @Override public Collection getHeaders(String name) { return serverResponse.headers().getAll(name); } @Override public Collection getHeaderNames() { return serverResponse.headers().names(); } @Override public void endResponse() { if (context == Vertx.currentContext()) { internalFlushBuffer(); return; } context.runOnContext(V -> internalFlushBuffer()); } public void internalFlushBuffer() { if (serverResponse.closed()) { return; } serverResponse.end(); } @Override public CompletableFuture sendPart(Part part) { DownloadUtils.prepareDownloadHeader(this, part); if (part == null) { return CompletableFuture.completedFuture(null); } return new PumpFromPart(context, part).toWriteStream(serverResponse, null); } @Override public CompletableFuture sendBuffer(Buffer buffer) { if (serverResponse.closed()) { return CompletableFuture.failedFuture(new IOException("Response is closed before sending any data. " + "Maybe client is timeout or check idle connection timeout for provider is properly configured.")); } CompletableFuture future = new CompletableFuture<>(); serverResponse.write(buffer).onComplete(result -> { if (result.failed()) { future.completeExceptionally(result.cause()); } else { future.complete(null); } }); return future; } @Override public void setChunked(boolean chunked) { serverResponse.setChunked(chunked); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import io.vertx.core.spi.metrics.ClientMetrics; import io.vertx.core.spi.observability.HttpRequest; import io.vertx.core.spi.observability.HttpResponse; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; public class DefaultClientMetrics implements ClientMetrics { private final DefaultClientEndpointMetric clientEndpointMetric; public DefaultClientMetrics(DefaultClientEndpointMetric clientEndpointMetric) { this.clientEndpointMetric = clientEndpointMetric; } public DefaultClientEndpointMetric getClientEndpointMetric() { return this.clientEndpointMetric; } @Override public DefaultRequestMetric requestBegin(String uri, HttpRequest request) { DefaultRequestMetric requestMetric = new DefaultRequestMetric(this.clientEndpointMetric); requestMetric.requestBegin(); return requestMetric; } @Override public void requestEnd(DefaultRequestMetric requestMetric, long bytesWritten) { requestMetric.requestEnd(); } @Override public void responseBegin(DefaultRequestMetric requestMetric, HttpResponse response) { requestMetric.responseBegin(); } @Override public void responseEnd(DefaultRequestMetric requestMetric, long bytesRead) { requestMetric.responseEnd(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.HttpClientMetrics; public class DefaultHttpClientMetrics implements HttpClientMetrics { private static final String PROTOCOL = "http://"; private final DefaultClientEndpointMetricManager clientEndpointMetricManager; public DefaultHttpClientMetrics(DefaultClientEndpointMetricManager clientEndpointMetricManager) { this.clientEndpointMetricManager = clientEndpointMetricManager; } @Override public DefaultClientMetrics createEndpointMetrics( SocketAddress remoteAddress, int maxPoolSize) { return new DefaultClientMetrics( getOrCreateEndpointMetric(remoteAddress)); } private DefaultClientEndpointMetric getOrCreateEndpointMetric(SocketAddress remoteAddress) { return this.clientEndpointMetricManager .getOrCreateEndpointMetric(PROTOCOL + remoteAddress.host() + ":" + remoteAddress.port()); } @Override public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric( getOrCreateEndpointMetric(remoteAddress)); socketMetric.onConnect(); return socketMetric; } @Override public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { socketMetric.onDisconnect(); } @Override public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); } @Override public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpServerMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.HttpServerMetrics; import io.vertx.core.spi.observability.HttpRequest; import io.vertx.core.spi.observability.HttpResponse; /** * important: not singleton, every HttpServer instance relate to an HttpServerMetrics instance */ public class DefaultHttpServerMetrics implements HttpServerMetrics { private final DefaultServerEndpointMetric endpointMetric; public DefaultHttpServerMetrics(DefaultServerEndpointMetric endpointMetric) { this.endpointMetric = endpointMetric; } public DefaultServerEndpointMetric getEndpointMetric() { return endpointMetric; } @Override public DefaultRequestMetric requestBegin(DefaultTcpSocketMetric socketMetric, HttpRequest request) { DefaultRequestMetric requestMetric = new DefaultRequestMetric(socketMetric.getEndpointMetric()); requestMetric.requestBegin(); return requestMetric; } @Override public void requestEnd(DefaultRequestMetric requestMetric, HttpRequest request, long bytesRead) { requestMetric.requestEnd(); } @Override public void responseBegin(DefaultRequestMetric requestMetric, HttpResponse response) { requestMetric.responseBegin(); } @Override public void responseEnd(DefaultRequestMetric requestMetric, HttpResponse response, long bytesWritten) { requestMetric.responseEnd(); } @Override public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric(endpointMetric); socketMetric.onConnect(); return socketMetric; } @Override public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { socketMetric.onDisconnect(); } @Override public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); } @Override public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpClientMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.TCPMetrics; /** * important: not singleton, every NetClient instance relate to a TCPMetrics instance */ public class DefaultTcpClientMetrics implements TCPMetrics { private static final String PROTOCOL = "tcp://"; private final DefaultClientEndpointMetricManager clientEndpointMetricManager; public DefaultTcpClientMetrics(DefaultClientEndpointMetricManager clientEndpointMetricManager) { this.clientEndpointMetricManager = clientEndpointMetricManager; } @Override public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { DefaultClientEndpointMetric endpointMetric = this.clientEndpointMetricManager .getOrCreateEndpointMetric(PROTOCOL + remoteAddress.toString()); DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric(endpointMetric); socketMetric.onConnect(); return socketMetric; } @Override public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { socketMetric.onDisconnect(); } @Override public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); } @Override public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpServerMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.TCPMetrics; /** * important: not singleton, every NetServer instance relate to a TcpServerMetrics instance */ public class DefaultTcpServerMetrics implements TCPMetrics { private final DefaultServerEndpointMetric endpointMetric; public DefaultTcpServerMetrics(DefaultServerEndpointMetric endpointMetric) { this.endpointMetric = endpointMetric; } public DefaultServerEndpointMetric getEndpointMetric() { return endpointMetric; } @Override public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric(endpointMetric); socketMetric.onConnect(); return socketMetric; } @Override public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { socketMetric.onDisconnect(); } @Override public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); } @Override public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import java.util.Map; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.HttpClientMetrics; import io.vertx.core.spi.metrics.HttpServerMetrics; import io.vertx.core.spi.metrics.TCPMetrics; import io.vertx.core.spi.metrics.VertxMetrics; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; public class DefaultVertxMetrics implements VertxMetrics { // to support listen multiple addresses, must use a map to manage the metric private final Map serverEndpointMetricMap = new ConcurrentHashMapEx<>(); private final DefaultClientEndpointMetricManager clientEndpointMetricManager; public DefaultVertxMetrics(VertxOptions vertxOptions) { this.clientEndpointMetricManager = new DefaultClientEndpointMetricManager( (MetricsOptionsEx) vertxOptions.getMetricsOptions()); } public DefaultClientEndpointMetricManager getClientEndpointMetricManager() { return clientEndpointMetricManager; } public Map getServerEndpointMetricMap() { return serverEndpointMetricMap; } @Override public HttpServerMetrics createHttpServerMetrics(HttpServerOptions options, SocketAddress localAddress) { DefaultServerEndpointMetric endpointMetric = serverEndpointMetricMap .computeIfAbsent(localAddress.toString(), DefaultServerEndpointMetric::new); return new DefaultHttpServerMetrics(endpointMetric); } @Override public HttpClientMetrics createHttpClientMetrics(HttpClientOptions options) { return new DefaultHttpClientMetrics(clientEndpointMetricManager); } @Override public TCPMetrics createNetServerMetrics(NetServerOptions options, SocketAddress localAddress) { DefaultServerEndpointMetric endpointMetric = serverEndpointMetricMap .computeIfAbsent(localAddress.toString(), DefaultServerEndpointMetric::new); return new DefaultTcpServerMetrics(endpointMetric); } @Override public TCPMetrics createNetClientMetrics(NetClientOptions options) { return new DefaultTcpClientMetrics(clientEndpointMetricManager); } @Override public boolean isMetricsEnabled() { return true; } public void setVertx(Vertx vertx) { clientEndpointMetricManager.setVertx(vertx); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.json.JsonObject; import io.vertx.core.metrics.MetricsOptions; import io.vertx.core.spi.VertxMetricsFactory; import io.vertx.core.spi.metrics.VertxMetrics; /** *
 * only for one vertx instance
 * DO NOT inject to vertx by SPI
 * typical usage:
 *     DefaultVertxMetricsFactory factory = new DefaultVertxMetricsFactory();
 *     MetricsOptionsEx metricsOptionsEx = (MetricsOptionsEx) factory.newOptions();
 *
 *     VertxOptions vertxOptions = new VertxOptions();
 *     vertxOptions.setMetricsOptions(metricsOptionsEx);
 *
 *     Vertx vertx = Vertx.vertx(vertxOptions);
 * 
*/ public class DefaultVertxMetricsFactory implements VertxMetricsFactory { private DefaultVertxMetrics vertxMetrics; public DefaultVertxMetrics getVertxMetrics() { return vertxMetrics; } @Override public synchronized VertxMetrics metrics(VertxOptions options) { if (vertxMetrics == null) { vertxMetrics = new DefaultVertxMetrics(options); } return vertxMetrics; } @Override public MetricsOptions newOptions() { MetricsOptionsEx metricsOptions = new MetricsOptionsEx(); metricsOptions.setEnabled(true); return metricsOptions; } @Override public MetricsOptions newOptions(JsonObject jsonObject) { return new MetricsOptionsEx(jsonObject); } @Override public MetricsOptions newOptions(MetricsOptions options) { return newOptions(options.toJson()); } public void setVertx(Vertx vertx, VertxOptions options) { if (vertxMetrics == null) { vertxMetrics = new DefaultVertxMetrics(options); } vertxMetrics.setVertx(vertx); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/MetricsOptionsEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import java.util.concurrent.TimeUnit; import io.vertx.core.json.JsonObject; import io.vertx.core.metrics.MetricsOptions; public class MetricsOptionsEx extends MetricsOptions { private long checkClientEndpointMetricIntervalInMilliseconds = TimeUnit.MINUTES.toMillis(1); private long checkClientEndpointMetricExpiredInNano = TimeUnit.MINUTES.toNanos(15); public MetricsOptionsEx() { super(); } public MetricsOptionsEx(JsonObject json) { super(json); } public long getCheckClientEndpointMetricIntervalInMilliseconds() { return checkClientEndpointMetricIntervalInMilliseconds; } public void setCheckClientEndpointMetricIntervalInMinute(long minute) { this.checkClientEndpointMetricIntervalInMilliseconds = TimeUnit.MINUTES.toMillis(minute); } public long getCheckClientEndpointMetricExpiredInNano() { return checkClientEndpointMetricExpiredInNano; } public void setCheckClientEndpointMetricExpiredInNano(long nanoTime) { this.checkClientEndpointMetricExpiredInNano = nanoTime; } public void setCheckClientEndpointMetricExpiredInMinute(long minute) { this.checkClientEndpointMetricExpiredInNano = TimeUnit.MINUTES.toNanos(minute); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; import java.util.concurrent.atomic.LongAdder; import com.google.common.annotations.VisibleForTesting; /** * for one listen address, include multiple httpClient or httpServer */ public class DefaultClientEndpointMetric extends DefaultEndpointMetric { private final LongAdder queue = new LongAdder(); // control if the metric instance will be expired // all invoker about incRefCount/isExpired, must lock: DefaultClientEndpointMetricManager // decRefCount no need to lock, because that only cause to be expired later. private volatile long lastNanoTime = System.nanoTime(); public DefaultClientEndpointMetric(String address) { super(address); } @VisibleForTesting public long getLastNanoTime() { return lastNanoTime; } @Override public void onDisconnect() { super.onDisconnect(); lastNanoTime = System.nanoTime(); } public long getQueueCount() { return queue.longValue(); } public void enqueueRequest() { queue.increment(); } public void dequeueRequest() { queue.decrement(); } public boolean isExpired(long nsTimeout) { return getCurrentConnectionCount() == 0 && (System.nanoTime() - lastNanoTime) > nsTimeout; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.vertx.metrics.MetricsOptionsEx; import com.google.common.annotations.VisibleForTesting; import io.vertx.core.Vertx; public class DefaultClientEndpointMetricManager { private final MetricsOptionsEx metricsOptionsEx; // to avoid save too many endpoint that not exist any more // must check expired periodically private final Map clientEndpointMetricMap = new ConcurrentHashMapEx<>(); // clientEndpointMetricMap is thread safe // but get/isExpired/remove is not safe // 1.isExpired // 2.get // 3.remove // will get a removed instance // so must lock the logic private final ReadWriteLock rwlock = new ReentrantReadWriteLock(); public DefaultClientEndpointMetricManager(MetricsOptionsEx metricsOptionsEx) { this.metricsOptionsEx = metricsOptionsEx; } public DefaultClientEndpointMetric getOrCreateEndpointMetric(String address) { rwlock.readLock().lock(); try { if (clientEndpointMetricMap.get(address) == null) { clientEndpointMetricMap.put(address, new DefaultClientEndpointMetric(address)); } return clientEndpointMetricMap.get(address); } finally { rwlock.readLock().unlock(); } } @VisibleForTesting public DefaultClientEndpointMetric getClientEndpointMetric(String serverAddress) { return clientEndpointMetricMap.get(serverAddress); } public Map getClientEndpointMetricMap() { return clientEndpointMetricMap; } @VisibleForTesting public void onCheckClientEndpointMetricExpired(long periodic) { for (DefaultClientEndpointMetric metric : clientEndpointMetricMap.values()) { if (metric.isExpired(metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano())) { rwlock.writeLock().lock(); try { if (metric.isExpired(metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano())) { clientEndpointMetricMap.remove(metric.getAddress()); } } finally { rwlock.writeLock().unlock(); } } } } public void setVertx(Vertx vertx) { vertx.setPeriodic(metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds(), this::onCheckClientEndpointMetricExpired); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientTaskMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; public class DefaultClientTaskMetric { private final DefaultClientEndpointMetric endpointMetric; public DefaultClientTaskMetric(DefaultClientEndpointMetric endpointMetric) { this.endpointMetric = endpointMetric; } public void enqueueRequest() { endpointMetric.enqueueRequest(); } public void dequeueRequest() { endpointMetric.dequeueRequest(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultEndpointMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; import java.util.concurrent.atomic.LongAdder; /** * for one listen address, include multiple httpClient or httpServer */ public class DefaultEndpointMetric { private final String address; // summary of connect times from boot // by this, we can know how many new connections connected recently private final LongAdder connectCount = new LongAdder(); // summary of disconnect times from boot // by this, we can know how many connections disconnected recently private final LongAdder disconnectCount = new LongAdder(); private final LongAdder bytesRead = new LongAdder(); private final LongAdder bytesWritten = new LongAdder(); private final LongAdder requests = new LongAdder(); private final LongAdder latency = new LongAdder(); public DefaultEndpointMetric(String address) { this.address = address; } public String getAddress() { return address; } public long getConnectCount() { return connectCount.longValue(); } public long getDisconnectCount() { return disconnectCount.longValue(); } public long getCurrentConnectionCount() { return connectCount.longValue() - disconnectCount.longValue(); } public void onConnect() { connectCount.increment(); } public void onDisconnect() { disconnectCount.increment(); } public long getBytesRead() { return bytesRead.longValue(); } public void addBytesRead(long bytes) { bytesRead.add(bytes); } public long getBytesWritten() { return bytesWritten.longValue(); } public void addBytesWritten(long bytes) { bytesWritten.add(bytes); } public void incrementRequests() { requests.increment(); } public long getRequests() { return requests.longValue(); } public void addLatency(long delta) { latency.add(delta); } public long getLatency() { return latency.longValue(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultRequestMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; public class DefaultRequestMetric { private long requestBeginTime; private long requestEndTime; private long responseBeginTime; private long responseEndTime; private final DefaultEndpointMetric endpointMetric; public DefaultRequestMetric(DefaultEndpointMetric endpointMetric) { this.endpointMetric = endpointMetric; } public long getRequestBeginTime() { return requestBeginTime != 0 ? requestBeginTime : System.nanoTime(); } public long getRequestEndTime() { return requestEndTime != 0 ? requestEndTime : System.nanoTime(); } public long getResponseBeginTime() { return responseBeginTime != 0 ? responseBeginTime : System.nanoTime(); } public long getResponseEndTime() { return responseEndTime != 0 ? responseEndTime : System.nanoTime(); } public void requestBegin() { this.requestBeginTime = System.nanoTime(); } public void requestEnd() { this.requestEndTime = System.nanoTime(); } public void responseBegin() { this.responseBeginTime = System.nanoTime(); } public void responseEnd() { this.responseEndTime = System.nanoTime(); this.endpointMetric.incrementRequests(); this.endpointMetric.addLatency(System.nanoTime() - this.requestBeginTime); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultServerEndpointMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; import java.util.concurrent.atomic.LongAdder; public class DefaultServerEndpointMetric extends DefaultEndpointMetric { private final LongAdder rejectByConnectionLimitCount = new LongAdder(); public DefaultServerEndpointMetric(String address) { super(address); } public long getRejectByConnectionLimitCount() { return rejectByConnectionLimitCount.longValue(); } public void onRejectByConnectionLimit() { rejectByConnectionLimitCount.increment(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultTcpSocketMetric.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics.metric; import com.google.common.annotations.VisibleForTesting; public class DefaultTcpSocketMetric { protected DefaultEndpointMetric endpointMetric; protected boolean connected = true; protected long connectedTime = System.nanoTime(); public DefaultTcpSocketMetric(DefaultEndpointMetric endpointMetric) { this.endpointMetric = endpointMetric; } public DefaultEndpointMetric getEndpointMetric() { return endpointMetric; } @VisibleForTesting public boolean isConnected() { return connected; } public void onConnect() { endpointMetric.onConnect(); this.connected = true; } public void onDisconnect() { endpointMetric.onDisconnect(); this.connected = false; } @VisibleForTesting public long getConnectedTime() { return connectedTime; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpBufferHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.server; import io.vertx.core.buffer.Buffer; /** * TcpBufferHandler * * */ public interface TcpBufferHandler { void handle(long msgId, Buffer headerBuffer, Buffer bodyBuffer); } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.server; import java.nio.charset.StandardCharsets; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.parsetools.RecordParser; public class TcpParser implements Handler { public static final byte[] TCP_MAGIC; public static final int TCP_HEADER_LENGTH = 23; static { TCP_MAGIC = "CSE.TCP".getBytes(StandardCharsets.UTF_8); } enum ParseStatus { TCP_HEADER, TCP_PAYLOAD } private final TcpBufferHandler outputHandler; private RecordParser parser; private ParseStatus status; private long msgId; // 仅仅是header + body,不包括headerLen本身 private int totalLen; private int headerLen; public TcpParser(TcpBufferHandler output) { this.outputHandler = output; reset(); } /** * 在解析出错时,通过重新创建parser对象,将整个缓冲区重置 */ protected void reset() { parser = RecordParser.newFixed(TCP_HEADER_LENGTH, this::onParse); status = ParseStatus.TCP_HEADER; parser.handle(Buffer.buffer(0)); } public boolean firstNEqual(byte[] a, byte[] b, int n) { assert a.length >= n && b.length >= n; for (int i = 0; i < n; i++) { if (a[i] != b[i]) { return false; } } return true; } protected void onParse(Buffer buffer) { switch (status) { case TCP_HEADER: if (!firstNEqual(TCP_MAGIC, buffer.getBytes(), TCP_MAGIC.length)) { reset(); return; } int index = TCP_MAGIC.length; msgId = buffer.getLong(index); index = index + 8; totalLen = buffer.getInt(index); index = index + 4; headerLen = buffer.getInt(index); if (headerLen > totalLen || headerLen <= 0) { throw new IllegalStateException("possibly attack."); } if (totalLen == 0) { onReadOnePackage(null, null); return; } parser.fixedSizeMode(totalLen); status = ParseStatus.TCP_PAYLOAD; break; case TCP_PAYLOAD: Buffer headerBuffer = buffer.slice(0, headerLen); Buffer bodyBuffer = buffer.slice(headerLen, buffer.length()); onReadOnePackage(headerBuffer, bodyBuffer); break; default: break; } } private void onReadOnePackage(Buffer headerBuffer, Buffer bodyBuffer) { outputHandler.handle(msgId, headerBuffer, bodyBuffer); parser.fixedSizeMode(TCP_HEADER_LENGTH); status = ParseStatus.TCP_HEADER; } public void handle(Buffer buf) { parser.handle(buf); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.server; import java.net.InetSocketAddress; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; import org.apache.servicecomb.foundation.vertx.AsyncResultCallback; import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; import org.apache.servicecomb.foundation.vertx.metrics.DefaultTcpServerMetrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.net.NetServer; import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.impl.NetSocketImpl; public class TcpServer { private static final Logger LOGGER = LoggerFactory.getLogger(TcpServer.class); private final URIEndpointObject endpointObject; public TcpServer(URIEndpointObject endpointObject) { this.endpointObject = endpointObject; } public void init(Vertx vertx, String sslKey, AsyncResultCallback callback) { NetServer netServer; if (endpointObject.isSslEnabled()) { SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, LegacyPropertyFactory.getEnvironment()); SSLOption sslOption; if (factory == null) { sslOption = SSLOption.build(sslKey, LegacyPropertyFactory.getEnvironment()); } else { sslOption = factory.createSSLOption(); } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); NetServerOptions serverOptions = new NetServerOptions(); VertxTLSBuilder.buildNetServerOptions(sslOption, sslCustom, serverOptions); netServer = vertx.createNetServer(serverOptions); } else { netServer = vertx.createNetServer(); } netServer.connectHandler(netSocket -> { DefaultTcpServerMetrics serverMetrics = (DefaultTcpServerMetrics) ((NetSocketImpl) netSocket).metrics(); DefaultServerEndpointMetric endpointMetric = serverMetrics.getEndpointMetric(); long connectedCount = endpointMetric.getCurrentConnectionCount(); int connectionLimit = getConnectionLimit(); if (connectedCount > connectionLimit) { netSocket.close(); endpointMetric.onRejectByConnectionLimit(); return; } TcpServerConnection connection = createTcpServerConnection(); connection.init(netSocket); }); netServer.exceptionHandler( e -> LOGGER.error("Unexpected error in server.", e)); InetSocketAddress socketAddress = endpointObject.getSocketAddress(); Future result = netServer.listen(socketAddress.getPort(), socketAddress.getHostString()); result.onComplete((s, f) -> { if (f == null) { callback.success(socketAddress); return; } // 监听失败 String msg = String.format("listen failed, address=%s", socketAddress); callback.fail(new Exception(msg, f)); }); } protected int getConnectionLimit() { return Integer.MAX_VALUE; } protected TcpServerConnection createTcpServerConnection() { return new TcpServerConnection(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServerConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.server; import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; public class TcpServerConnection extends TcpConnection { private static final Logger LOGGER = LoggerFactory.getLogger(TcpServerConnection.class); protected TcpParser splitter; public void init(NetSocket netSocket) { // currently, socket always be NetSocketImpl this.initNetSocket((NetSocketImpl) netSocket); String remoteAddress = netSocket.remoteAddress().toString(); LOGGER.info("connect from {}, in thread {}", remoteAddress, Thread.currentThread().getName()); netSocket.exceptionHandler(e -> LOGGER.error("disconnected from {}, in thread {}, cause {}", remoteAddress, Thread.currentThread().getName(), e.getMessage())); netSocket.closeHandler(Void -> LOGGER.info("disconnected from {}, in thread {}", remoteAddress, Thread.currentThread().getName())); netSocket.handler(splitter); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.io.IOException; import java.nio.charset.StandardCharsets; import io.vertx.core.buffer.Buffer; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; public class BufferInputStream extends ServletInputStream { private int readIndex = 0; private final Buffer byteBuf; public BufferInputStream(Buffer buffer) { this.byteBuf = buffer; } @Override public long skip(long len) { int skipLen = Math.min((int) len, available()); this.readIndex += skipLen; return skipLen; } public byte readByte() { int index = readIndex; readIndex = index + 1; return byteBuf.getByte(index); } @Override public int read() { return this.readByte() & 255; } public boolean readBoolean() { return this.readByte() != 0; } public short readShort() { int index = readIndex; readIndex = index + 2; return byteBuf.getShort(index); } public int readInt() { int index = readIndex; readIndex = index + 4; return byteBuf.getInt(index); } public long readLong() { int index = readIndex; readIndex = index + 8; return byteBuf.getLong(index); } public int getIndex() { return readIndex; } public String readString() { int length = readInt(); byte[] bytes = new byte[length]; int index = readIndex; readIndex = index + length; byteBuf.getBytes(index, readIndex, bytes); return new String(bytes, StandardCharsets.UTF_8); } @Override public int read(byte[] b) { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) { int avail = available(); if (avail <= 0) { return -1; } if (len == 0) { return 0; } if (len > avail) { len = avail; } int index = readIndex; readIndex = index + len; byteBuf.getBytes(index, readIndex, b); return len; } @Override public int available() { return byteBuf.length() - readIndex; } @Override public void close() { // nothing to do } @Override public void reset() throws IOException { readIndex = 0; } public Buffer getByteBuf() { return byteBuf; } @Override public boolean isFinished() { return byteBuf.length() > readIndex; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import io.vertx.core.buffer.Buffer; /** * BufferOutputStream. * * Notice: will not release the underlining ByteBuffer, the user is responsible to release it. */ public class BufferOutputStream extends OutputStream { private static final int DIRECT_BUFFER_SIZE = 1024; protected Buffer byteBuf; public BufferOutputStream() { this(Buffer.buffer(DIRECT_BUFFER_SIZE)); } public BufferOutputStream(Buffer buffer) { this.byteBuf = buffer; } public Buffer getBuffer() { return byteBuf; } public int length() { return byteBuf.length(); } public void writeByte(byte value) { byteBuf.appendByte(value); } // 实际是写byte @Override public void write(int byteValue) { byteBuf.appendByte((byte) byteValue); } public void write(boolean value) { byteBuf.appendByte(value ? (byte) 1 : (byte) 0); } public void writeInt(int pos, int value) { byteBuf.setInt(pos, value); } public void writeShort(short value) { byteBuf.appendShort(value); } public void writeInt(int value) { byteBuf.appendInt(value); } public void writeLong(long value) { byteBuf.appendLong(value); } public void writeString(String value) { writeInt(value.length()); byteBuf.appendString(value, StandardCharsets.UTF_8.toString()); } @Override public void write(byte[] b) { write(b, 0, b.length); } @Override public void write(byte[] bytes, int offset, int len) { byteBuf.appendBytes(bytes, offset, len); } @Override public void close() { // Do no release byteBuf, the target BufferedInputStream will release it. } public int writerIndex() { return byteBuf.length(); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.buffer.Buffer; import io.vertx.core.streams.ReadStream; public class InputStreamToReadStream implements ReadStream { private static final Logger LOGGER = LoggerFactory.getLogger(InputStreamToReadStream.class); public static final int DEFAULT_READ_BUFFER_SIZE = 1024 * 1024; private final Context context; private final InputStream inputStream; private volatile boolean closed; private boolean paused; private boolean readInProgress; private int readBufferSize = DEFAULT_READ_BUFFER_SIZE; private Handler exceptionHandler = this::unhandledException; private Handler dataHandler; private Handler endHandler; private final boolean autoCloseInputStream; public InputStreamToReadStream(Context context, InputStream inputStream, boolean autoCloseInputStream) { this.context = context; this.inputStream = inputStream; this.autoCloseInputStream = autoCloseInputStream; } public synchronized InputStreamToReadStream readBufferSize(int readBufferSize) { this.readBufferSize = readBufferSize; return this; } private void check() { if (closed) { throw new IllegalStateException("inputStream is closed"); } } private void unhandledException(Throwable t) { LOGGER.error("Unhandled exception", t); } @Override public synchronized InputStreamToReadStream exceptionHandler(Handler handler) { check(); this.exceptionHandler = handler; return this; } @Override public synchronized InputStreamToReadStream handler(Handler handler) { check(); this.dataHandler = handler; if (dataHandler != null && !paused && !closed) { doRead(); } return this; } class ReadResult { int read; byte[] bytes = new byte[readBufferSize]; void doRead() throws IOException { read = inputStream.read(bytes); } Buffer toBuffer() { return Buffer.buffer(Arrays.copyOf(bytes, read)); } } private synchronized void doRead() { if (!readInProgress) { readInProgress = true; Promise future = Promise.promise(); context.executeBlocking(() -> { readInWorker(future); return null; }, true); future.future().onComplete(this::afterReadInEventloop); } } private synchronized void readInWorker(Promise future) { try { ReadResult readResult = new ReadResult(); readResult.doRead(); future.complete(readResult); } catch (Throwable e) { future.fail(e); } } public void handleException(Throwable e) { closeInputStream(); exceptionHandler.handle(e); } private synchronized void afterReadInEventloop(ReadResult readResult, Throwable failure) { if (failure != null) { handleException(failure); return; } readInProgress = false; if (readResult.read < 0) { handleEnd(); return; } handleData(readResult.toBuffer()); if (!paused && dataHandler != null) { doRead(); } } @Override public synchronized InputStreamToReadStream pause() { check(); paused = true; return this; } @Override public synchronized InputStreamToReadStream resume() { check(); if (paused && !closed) { paused = false; if (dataHandler != null) { doRead(); } } return this; } private synchronized void handleData(Buffer buffer) { if (dataHandler != null) { dataHandler.handle(buffer); } } private synchronized void handleEnd() { dataHandler = null; closeInputStream(); if (endHandler != null) { endHandler.handle(null); } } private void closeInputStream() { if (closed) { return; } closed = true; if (!autoCloseInputStream) { return; } try { inputStream.close(); } catch (IOException e) { LOGGER.error("failed to close inputSteam.", e); } } @Override public ReadStream endHandler(Handler handler) { check(); this.endHandler = handler; return this; } @Override public ReadStream fetch(long amount) { return this; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.io.IOException; import java.io.OutputStream; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.common.io.AsyncCloseable; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.buffer.Buffer; import io.vertx.core.streams.WriteStream; /** * for pump from a readStream */ public class OutputStreamToWriteStream implements WriteStream, AsyncCloseable { private static final int DEFAULT_MAX_BUFFERS = 4; private static final int SMALLEST_MAX_BUFFERS = 2; private final OutputStream outputStream; private final Context context; private final boolean autoCloseOutputStream; private Handler exceptionHandler; // resume readStream private Handler drainHandler; // when invoke close, but outputStream not write all data, must put close logic to closedDeferred private Runnable closedDeferred; private boolean closed; // buffers.size() need to loop all node, and maybe result is not correct in concurrent condition // we just need to flow control by pump, so use another size private final Queue buffers = new ConcurrentLinkedQueue<>(); private final AtomicInteger currentBufferCount = new AtomicInteger(); // just indicate if buffers is full, not control add logic // must >= SMALLEST_MAX_BUFFERS // if < SMALLEST_MAX_BUFFERS, then maxBuffers will be SMALLEST_MAX_BUFFERS private int maxBuffers = DEFAULT_MAX_BUFFERS; // if currentBufferCount <= drainMark, will invoke drainHandler to resume readStream private int drainMark = maxBuffers / 2; public OutputStreamToWriteStream(Context context, OutputStream outputStream, boolean autoCloseOutputStream) { this.context = context; this.outputStream = outputStream; this.autoCloseOutputStream = autoCloseOutputStream; } @Override public WriteStream exceptionHandler(Handler handler) { this.exceptionHandler = handler; return this; } private void handleException(Throwable t) { if (exceptionHandler != null) { exceptionHandler.handle(t); } } @Override public synchronized Future write(Buffer data) { Promise result = Promise.promise(); write(data, result); return result.future(); } private void write(Buffer data, Promise future) { currentBufferCount.incrementAndGet(); buffers.add(data); context.executeBlocking( () -> { writeInWorker(future); return null; }, true ); } protected void writeInWorker(Promise future) { while (true) { Buffer buffer = buffers.poll(); if (buffer == null) { future.complete(); return; } try { outputStream.write(buffer.getBytes()); synchronized (OutputStreamToWriteStream.this) { currentBufferCount.decrementAndGet(); Runnable action = (currentBufferCount.get() == 0 && closedDeferred != null) ? closedDeferred : this::checkDrained; action.run(); } } catch (IOException e) { currentBufferCount.decrementAndGet(); future.fail(e); return; } } } @Override public Future end() { return Future.fromCompletionStage(close()); } @Override public WriteStream setWriteQueueMaxSize(int maxSize) { this.maxBuffers = Math.max(maxSize, SMALLEST_MAX_BUFFERS); this.drainMark = maxBuffers / 2; return this; } @Override public synchronized boolean writeQueueFull() { return currentBufferCount.get() >= maxBuffers; } @Override public synchronized WriteStream drainHandler(Handler handler) { this.drainHandler = handler; return this; } private synchronized void checkDrained() { if (drainHandler != null && currentBufferCount.get() <= drainMark) { Handler handler = drainHandler; drainHandler = null; handler.handle(null); } } @Override public CompletableFuture close() { return closeInternal(); } private void check() { checkClosed(); } private void checkClosed() { if (closed) { throw new IllegalStateException(this.getClass().getName() + " is closed"); } } private synchronized CompletableFuture closeInternal() { check(); closed = true; CompletableFuture future = new CompletableFuture<>(); if (currentBufferCount.get() == 0) { doClose(future); } else { closedDeferred = () -> doClose(future); } return future; } private void doClose(CompletableFuture future) { if (autoCloseOutputStream) { try { outputStream.close(); } catch (IOException e) { future.completeExceptionally(new IllegalStateException("failed to close outputStream.", e)); return; } } future.complete(null); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.foundation.common.io.AsyncCloseable; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.WriteStream; import io.vertx.core.streams.impl.PipeImpl; public class PumpCommon { /** * @return future of save action
*

important: *

if writeStream is AsyncCloseable, future means write complete *

if writeStream is not AsyncCloseable, future only means read complete */ @SuppressWarnings("unchecked") public CompletableFuture pump(ReadStream readStream, WriteStream writeStream, Handler throwableHandler) { CompletableFuture readFuture = new CompletableFuture<>(); writeStream.exceptionHandler(e -> { // consumer -> producer // 3rd consumer -> edge -> producer // when download not finished, consumer stop download // producer should stop download logic at once if (readStream instanceof InputStreamToReadStream) { ((InputStreamToReadStream) readStream).handleException(e); } else if (readStream instanceof HttpClientResponse) { // can not find a way to cancel/terminate request // so can only close the connection. ((HttpClientResponse) readStream).request().connection().close(); } if (throwableHandler != null) { throwableHandler.handle(e); } readFuture.completeExceptionally(e); }); readStream.exceptionHandler(readFuture::completeExceptionally); Future pipeResult = new PipeImpl<>(readStream).endOnComplete(false).to(writeStream); pipeResult.onComplete((s) -> readFuture.complete(null), (f) -> { if (throwableHandler != null) { throwableHandler.handle(f); } readFuture.completeExceptionally(f); }); if (!(writeStream instanceof AsyncCloseable)) { return readFuture; } return closeWriteStream((AsyncCloseable) writeStream, readFuture); } protected CompletableFuture closeWriteStream(AsyncCloseable writeStream, CompletableFuture readFuture) { CompletableFuture writeFuture = new CompletableFuture<>(); readFuture.whenComplete((v, e) -> writeStream.close().whenComplete((wv, we) -> { if (we != null) { writeFuture.completeExceptionally(we); return; } writeFuture.complete(null); }) ); return CompletableFuture.allOf(readFuture, writeFuture); } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.CompletableFuture; import jakarta.servlet.http.Part; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.vertx.http.DownloadUtils; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.WriteStream; public class PumpFromPart { private static final Logger LOGGER = LoggerFactory.getLogger(PumpFromPart.class); private final Context context; private final Part part; public PumpFromPart(Context context, Part part) { this.context = context; this.part = part; } private CompletableFuture> prepareReadStream() { CompletableFuture> future = new CompletableFuture<>(); if (ReadStreamPart.class.isInstance(part)) { future.complete(((ReadStreamPart) part).getReadStream()); return future; } try { InputStream inputStream = part.getInputStream(); InputStreamToReadStream inputStreamToReadStream = new InputStreamToReadStream(context, inputStream, true); inputStreamToReadStream.pause(); future.complete(inputStreamToReadStream); } catch (IOException e) { future.completeExceptionally(e); } return future; } public CompletableFuture toWriteStream(WriteStream writeStream, Handler throwableHandler) { return prepareReadStream() .thenCompose(readStream -> new PumpCommon().pump(readStream, writeStream, throwableHandler)) .whenComplete((v, e) -> { if (e != null) { LOGGER.error("to write stream failed.", e); } DownloadUtils.clearPartResource(part); // PumpImpl will add drainHandler to writeStream, // in order to support write multiple files to same writeStream, // need reset after one stream is successful. writeStream.drainHandler(null); }); } public CompletableFuture toOutputStream(OutputStream outputStream, boolean autoCloseOutputStream) { if (context == null) { return toOutputStreamSync(outputStream, autoCloseOutputStream); } return toOutputStreamAsync(outputStream, autoCloseOutputStream); } private CompletableFuture toOutputStreamAsync(OutputStream outputStream, boolean autoCloseOutputStream) { OutputStreamToWriteStream outputStreamToWriteStream = new OutputStreamToWriteStream(context, outputStream, autoCloseOutputStream); return toWriteStream(outputStreamToWriteStream, null); } // DO NOT use a mocked sync context to unify the pump logic // otherwise when pump big stream, will cause stack overflow private CompletableFuture toOutputStreamSync(OutputStream outputStream, boolean autoCloseOutputStream) { CompletableFuture future = new CompletableFuture<>(); future.whenComplete((v, e) -> DownloadUtils.clearPartResource(part)); try (InputStream inputStream = part.getInputStream()) { IOUtils.copyLarge(inputStream, outputStream); } catch (Throwable e) { future.completeExceptionally(e); } if (autoCloseOutputStream) { try { outputStream.close(); } catch (Throwable e) { future.completeExceptionally(e); } } future.complete(null); return future; } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.tcp; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicLong; import io.vertx.core.Context; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; public class TcpConnection { protected String protocol; // 压缩算法名字 protected String zipName; protected NetSocket netSocket; // context of netSocket protected Context context; // vertx's wrap for netty is ugly: write always lock first // so we save buf in a CAS queue, and notify eventloop thread to do the real write // // netty: // if write caller is not in network IO thread // always wrap the write as a task, and put it to IO thread queue(MPSC queue) // // so this optimization: // 1.avoid vertx's lock // 2.reduce netty's task schedule private final Queue writeQueue = new ConcurrentLinkedQueue<>(); private final AtomicLong writeQueueSize = new AtomicLong(); public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getZipName() { return zipName; } public void setZipName(String zipName) { this.zipName = zipName; } public void setContext(Context context) { this.context = context; } public NetSocket getNetSocket() { return netSocket; } public void initNetSocket(NetSocketImpl netSocket) { this.netSocket = netSocket; this.context = netSocket.context(); } public void write(Buffer buf) { writeQueue.add(buf); long oldSize = writeQueueSize.getAndIncrement(); if (oldSize == 0) { scheduleWrite(); } } // notify context thread to write protected void scheduleWrite() { context.runOnContext(v -> writeInContext()); } protected void writeInContext() { for (; ; ) { Buffer buf = writeQueue.poll(); if (buf == null) { break; } writeQueueSize.decrementAndGet(); netSocket.write(buf); } } } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.tcp; public final class TcpConst { private TcpConst() { } public static final String LOGIN = "login"; } ================================================ FILE: foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.tcp; import org.apache.servicecomb.foundation.vertx.server.TcpParser; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; /** * TcpOutputStream * * */ public class TcpOutputStream extends BufferOutputStream { private final long msgId; public TcpOutputStream(long msgId) { super(); this.msgId = msgId; write(TcpParser.TCP_MAGIC); writeLong(msgId); } public long getMsgId() { return msgId; } public void writeLength(int totalLen, int headerLen) { writeInt(totalLen); writeInt(headerLen); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.impl.SysProps; public class TestSharedVertxFactory { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } @Test public void getTransportVertx() { Assertions.assertNotNull(SharedVertxFactory.getSharedVertx(environment)); Assertions.assertSame(SharedVertxFactory.getSharedVertx(environment), SharedVertxFactory.getSharedVertx(environment)); SharedVertxFactory.getSharedVertx(environment).close(); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleBodyHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import jakarta.ws.rs.core.Response.Status; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.impl.RoutingContextInternal; public class TestSimpleBodyHandler { private SimpleBodyHandler instance; private RoutingContextInternal context; @Before public void setUp() throws Exception { context = Mockito.mock(RoutingContextInternal.class); HttpServerRequest request = Mockito.mock(HttpServerRequest.class); Mockito.when(context.request()).thenReturn(request); MultiMap multiMap = Mockito.mock(MultiMap.class); Mockito.when(request.headers()).thenReturn(multiMap); HttpServerResponse response = Mockito.mock(HttpServerResponse.class); Mockito.when(response.setStatusCode(Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode())).thenReturn(response); Mockito.when(context.response()).thenReturn(response); } @After public void tearDown() throws Exception { instance = null; context = null; } @Test public void testValidContentType() { instance = new SimpleBodyHandler() { @Override protected boolean contentTypeSupported(String contentType) { return true; } }; instance.handle(context); Assertions.assertTrue(instance.checkContentType(context)); } @Test public void testInvalidContentType() { instance = new SimpleBodyHandler() { @Override protected boolean contentTypeSupported(String contentType) { return false; } }; instance.handle(context); Assertions.assertFalse(instance.checkContentType(context)); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleJsonObject.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestSimpleJsonObject { @Test public void testPutObject() { Object value = new Object(); SimpleJsonObject json = new SimpleJsonObject(); json.put("k", value); Assertions.assertSame(value, json.getValue("k")); } @Test public void testCopy() { SimpleJsonObject json = new SimpleJsonObject(); Assertions.assertSame(json, json.copy()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.vertx.core.buffer.Buffer; public class TestStream { private static final int DIRECT_BUFFER_SIZE = 1024; @Test public void testBufferInputStream() { Buffer obuf = Buffer.buffer(DIRECT_BUFFER_SIZE); obuf.appendBytes(("tests1").getBytes()); @SuppressWarnings("resource") BufferInputStream oBufferInputStream = new BufferInputStream(obuf); byte test = oBufferInputStream.readByte(); Assertions.assertEquals((byte) 't', test); Assertions.assertEquals((byte) 'e', oBufferInputStream.read()); Assertions.assertEquals(2, oBufferInputStream.skip(2)); Assertions.assertEquals((byte) 's', oBufferInputStream.read()); Assertions.assertTrue(oBufferInputStream.readBoolean()); Assertions.assertEquals(6, oBufferInputStream.getIndex()); Assertions.assertEquals(0, oBufferInputStream.available()); } @Test public void testBufferOutputStream() { @SuppressWarnings({"resource"}) BufferOutputStream oBufferOutputStream = new BufferOutputStream(); oBufferOutputStream.writeString("test"); oBufferOutputStream.write(1); oBufferOutputStream.write(true); Assertions.assertTrue((1 < oBufferOutputStream.length())); @SuppressWarnings("resource") BufferInputStream oBufferInputStream = new BufferInputStream(oBufferOutputStream.getBuffer()); Assertions.assertEquals("test", oBufferInputStream.readString()); Assertions.assertEquals(1, oBufferInputStream.readByte()); Assertions.assertTrue(oBufferInputStream.readBoolean()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxTLSBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.http.ClientAuth; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; import mockit.Mock; import mockit.MockUp; public class TestVertxTLSBuilder { Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { LegacyPropertyFactory.setEnvironment(environment); } @Test public void testBuildHttpServerOptions() { SSLOption option = SSLOption.build("rest.provider", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpServerOptions serverOptions = new HttpServerOptions(); VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertEquals(serverOptions.getClientAuth(), ClientAuth.REQUEST); } @Test public void testBuildHttpClientOptions_sslKey_noFactory() { HttpClientOptions clientOptions = new HttpClientOptions(); VertxTLSBuilder.buildHttpClientOptions("notExist", clientOptions); Assertions.assertTrue(clientOptions.isSsl()); } public static class SSLOptionFactoryForTest implements SSLOptionFactory { static SSLOption sslOption = new SSLOption(); static { sslOption.setEngine("openssl"); sslOption.setProtocols(""); sslOption.setCiphers(SSLOption.DEFAULT_CIPHERS); sslOption.setCheckCNHost(true); } @Override public SSLOption createSSLOption() { return sslOption; } } @Test public void testBuildHttpClientOptions_ssl_withFactory() { Mockito.when(environment.getProperty("ssl.exist.sslOptionFactory")) .thenReturn(SSLOptionFactoryForTest.class.getName()); HttpClientOptions clientOptions = new HttpClientOptions(); VertxTLSBuilder.buildHttpClientOptions("exist", clientOptions); Assertions.assertTrue(clientOptions.isSsl()); Assertions.assertTrue(clientOptions.isVerifyHost()); } @Test public void testBuildHttpClientOptions() { SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); VertxTLSBuilder.buildHttpClientOptions(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertTrue(serverOptions.isTrustAll()); } @Test public void testBuildClientOptionsBase() { SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertTrue(serverOptions.isTrustAll()); } @Test public void testBuildClientOptionsBaseFileNull() { SSLOption option = SSLOption.build("rest.consumer", environment); option.setKeyStore(null); option.setTrustStore(null); option.setCrl(null); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertTrue(serverOptions.isTrustAll()); } @Test public void testBuildClientOptionsBaseAuthPeerFalse() { SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); new MockUp() { @Mock public boolean isAuthPeer() { return false; } }; VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertTrue(serverOptions.isTrustAll()); } @Test public void testBuildClientOptionsBaseSTORE_JKS() { SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); new MockUp() { @Mock public String getKeyStoreType() { return "JKS"; } }; VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertTrue(serverOptions.isTrustAll()); } @Test public void testBuildClientOptionsBaseSTORE_PKCS12() { SSLOption option = SSLOption.build("rest.consumer", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpClientOptions serverOptions = new HttpClientOptions(); new MockUp() { @Mock public String getTrustStoreType() { return "PKCS12"; } }; VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertTrue(serverOptions.isTrustAll()); } @Test public void testBuildHttpServerOptionsRequest() { SSLOption option = SSLOption.build("rest.provider", environment); SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); HttpServerOptions serverOptions = new HttpServerOptions(); new MockUp() { @Mock public boolean isAuthPeer() { return false; } }; VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); Assertions.assertEquals(serverOptions.getClientAuth(), ClientAuth.REQUEST); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.CountDownLatch; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; import io.vertx.core.impl.SysProps; public class TestVertxUtils { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } @Test public void testGetOrCreateVertx() throws InterruptedException { Vertx vertx = VertxUtils.getOrCreateVertxByName("ut", null, null); Holder name = new Holder<>(); CountDownLatch latch = new CountDownLatch(1); vertx.runOnContext(v -> { name.value = Thread.currentThread().getName(); latch.countDown(); }); latch.await(); Assertions.assertEquals(name.value, "ut-vert.x-eventloop-thread-0"); VertxUtils.blockCloseVertxByName("ut"); } @Test public void testCreateVertxWithFileCPResolving() { // create .vertx folder Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(false); deleteCacheFile(); VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingFalse", null, null); Assertions.assertTrue(isCacheFileExists()); VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingFalse"); // don't create .vertx folder deleteCacheFile(); Assertions.assertFalse(isCacheFileExists()); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingTrue", null, null); Assertions.assertFalse(isCacheFileExists()); VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingTrue"); } private void deleteCacheFile() { String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, System.getProperty("java.io.tmpdir", ".")); File folder = new File(cacheDirBase); File[] files = folder.listFiles(); for (File f : files) { if (f.getName().startsWith("vertx-cache")) { FileUtils.deleteQuietly(f); } } } private boolean isCacheFileExists() { String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, System.getProperty("java.io.tmpdir", ".")); File folder = new File(cacheDirBase); File[] files = folder.listFiles(); for (File f : files) { if (f.getName().startsWith("vertx-cache")) { return true; } } return false; } @Test public void testVertxUtilsInitNullOptions() { Vertx vertx = VertxUtils.init(null, null, null); Assertions.assertNotEquals(null, vertx); VertxUtils.blockCloseVertx(vertx); } @Test public void testVertxUtilsInitWithOptions() { VertxOptions oOptions = new VertxOptions(); Vertx vertx = VertxUtils.init(null, oOptions, null); Assertions.assertNotEquals(null, vertx); VertxUtils.blockCloseVertx(vertx); } @Test public void testgetBytesFastBufferInputStream() throws IOException { byte[] bytes = new byte[] {1}; Buffer byteBuf = Buffer.buffer(bytes); try (BufferInputStream inputStream = new BufferInputStream(byteBuf)) { byte[] result = VertxUtils.getBytesFast(inputStream); Assertions.assertEquals(bytes.length, result.length); Assertions.assertEquals(bytes[0], result[0]); } } @Test public void testgetBytesFastNormalInputStream() throws IOException { byte[] bytes = new byte[] {1}; try (InputStream inputStream = new ByteArrayInputStream(bytes)) { byte[] result = VertxUtils.getBytesFast(inputStream); Assertions.assertEquals(1, result[0]); } } @Test public void testgetBytesFastBuffer() { Buffer buffer = Buffer.buffer(); buffer.appendByte((byte) 1); byte[] result = VertxUtils.getBytesFast(buffer); Assertions.assertEquals(1, result[0]); } @Test public void testgetBytesFastByteBufHasArray() { byte[] bytes = new byte[] {1}; ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); byte[] result = VertxUtils.getBytesFast(byteBuf); Assertions.assertSame(bytes, result); } @Test public void testgetBytesFastByteBufCopy() { ByteBuf byteBuf = Unpooled.directBuffer(); byteBuf.writeByte(1); Assertions.assertFalse(byteBuf.hasArray()); byte[] result = VertxUtils.getBytesFast(byteBuf); Assertions.assertEquals(1, result[0]); byteBuf.release(); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientPoolManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.core.impl.VertxImpl; import mockit.Deencapsulation; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestClientPoolManager { @Mocked Vertx vertx; @Mocked ClientPoolFactory factory; ClientPoolManager poolMgr; String id; List pools; Map contextMap = new HashMap<>(); @Mocked Context context; @Before public void setup() { poolMgr = new ClientPoolManager<>(vertx, factory); id = Deencapsulation.getField(poolMgr, "id"); pools = Deencapsulation.getField(poolMgr, "pools"); new MockUp(context) { @Mock void put(Object key, Object value) { contextMap.put(key, value); } @SuppressWarnings("unchecked") @Mock T get(Object key) { return (T) contextMap.get(key); } @Mock Vertx owner() { return vertx; } @Mock boolean isEventLoopContext() { return true; } }; } @Test public void createClientPool(@Mocked HttpClientWithContext pool) { new Expectations(VertxImpl.class) { { factory.createClientPool(context); result = pool; } }; Assertions.assertSame(pool, poolMgr.createClientPool(context)); Assertions.assertSame(pool, context.get(id)); MatcherAssert.assertThat(pools, Matchers.contains(pool)); } @Test public void findClientPool_sync(@Mocked HttpClientWithContext pool1, @Mocked HttpClientWithContext pool2) { new Expectations(poolMgr) { { poolMgr.findThreadBindClientPool(); result = pool1; poolMgr.findByContext(null); result = pool2; } }; Assertions.assertSame(pool1, poolMgr.findClientPool(true)); Assertions.assertSame(pool2, poolMgr.findClientPool(false)); } @Test public void findThreadBindClientPool(@Mocked HttpClientWithContext pool1, @Mocked HttpClientWithContext pool2) { pools.add(pool1); pools.add(pool2); new MockUp() { @Mock long getId() { return 0; } }; Assertions.assertSame(pool1, poolMgr.findThreadBindClientPool()); // find again, get the same result Assertions.assertSame(pool1, poolMgr.findThreadBindClientPool()); new MockUp() { @Mock long getId() { return 1; } }; Assertions.assertSame(pool2, poolMgr.findThreadBindClientPool()); // find again, get the same result Assertions.assertSame(pool2, poolMgr.findThreadBindClientPool()); } @Test public void findByContext_reactive() { HttpClientWithContext notMatchPool1 = new HttpClientWithContext(null, null); HttpClientWithContext notMatchPool2 = new HttpClientWithContext(null, null); pools.add(notMatchPool1); pools.add(notMatchPool2); new Expectations() { { Vertx.currentContext(); result = context; } }; context.put(id, notMatchPool2); Assertions.assertSame(notMatchPool2, poolMgr.findByContext()); // find again, get the same result Assertions.assertSame(notMatchPool2, poolMgr.findByContext()); } @Test public void findByContext_wrongContext_reverse() { HttpClientWithContext pool1 = new HttpClientWithContext(null, null); HttpClientWithContext pool2 = new HttpClientWithContext(null, null); pools.add(pool1); pools.add(pool2); new Expectations() { { Vertx.currentContext(); result = null; } }; AtomicInteger reactiveNextIndex = Deencapsulation.getField(poolMgr, "reactiveNextIndex"); reactiveNextIndex.set(Integer.MAX_VALUE); // each time invoke find, reactiveNextIndex will inc 1 Assertions.assertSame(pool2, poolMgr.findByContext()); Assertions.assertSame(pool1, poolMgr.findByContext()); Assertions.assertSame(pool2, poolMgr.findByContext()); Assertions.assertSame(pool1, poolMgr.findByContext()); } @Test public void findByContext_normalThread() { HttpClientWithContext pool = new HttpClientWithContext(null, null); pools.add(pool); new Expectations() { { Vertx.currentContext(); result = null; } }; Assertions.assertSame(pool, poolMgr.findByContext()); } @Test public void findByContext_otherVertx(@Mocked VertxImpl otherVertx, @Mocked Context otherContext) { HttpClientWithContext pool = new HttpClientWithContext(null, null); pools.add(pool); new Expectations() { { Vertx.currentContext(); result = otherContext; otherContext.owner(); result = otherVertx; } }; Assertions.assertSame(pool, poolMgr.findByContext()); } @Test public void findByContext_worker(@Mocked Context workerContext) { HttpClientWithContext pool = new HttpClientWithContext(null, null); pools.add(pool); new Expectations() { { Vertx.currentContext(); result = workerContext; workerContext.owner(); result = vertx; workerContext.isEventLoopContext(); result = false; } }; Assertions.assertSame(pool, poolMgr.findByContext()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.vertx.SimpleJsonObject; import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; import org.junit.Test; import io.vertx.core.Context; import io.vertx.core.json.JsonObject; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestClientVerticle { ClientVerticle clientVerticle = new ClientVerticle<>(); @Test public void start(@Mocked Context context) throws Exception { AtomicInteger count = new AtomicInteger(); ClientPoolManager clientMgr = new MockUp>() { @Mock HttpClientWithContext createClientPool(Context context) { count.incrementAndGet(); return null; } }.getMockInstance(); clientVerticle.init(null, context); JsonObject config = new SimpleJsonObject(); config.put(ClientVerticle.CLIENT_MGR, clientMgr); new Expectations() { { context.config(); result = config; } }; clientVerticle.start(); Assertions.assertEquals(1, count.get()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestAbstractTcpClientPoolFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import org.junit.jupiter.api.Assertions; import io.vertx.core.Context; import io.vertx.core.Vertx; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestAbstractTcpClientPoolFactory { private final TcpClientConfig normalClientConfig = new TcpClientConfig(); private final TcpClientConfig sslClientConfig = new TcpClientConfig(); TcpClientPoolFactory factory = new TcpClientPoolFactory(normalClientConfig, sslClientConfig); @Test public void createClientPool() { Vertx vertx = Mockito.mock(Vertx.class); Context context = Mockito.mock(Context.class); Mockito.when(context.owner()).thenReturn(vertx); TcpClientConnectionPool pool = factory.createClientPool(context); Assertions.assertSame(normalClientConfig, pool.netClientWrapper.getClientConfig(false)); Assertions.assertSame(sslClientConfig, pool.netClientWrapper.getClientConfig(true)); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTcpClientConfig { @Test public void testTcpClientConfig() { TcpClientConfig config = new TcpClientConfig(); Assertions.assertEquals(config.getMsLoginTimeout(), 30000); Assertions.assertFalse(config.isSsl()); config.setMsLoginTimeout(500); Assertions.assertEquals(config.getMsLoginTimeout(), 500); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.client.tcp; import java.util.Map; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConnection.Status; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.netty.buffer.ByteBuf; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; import mockit.Deencapsulation; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestTcpClientConnection { @Mocked Context context; @Mocked NetClientWrapper netClientWrapper; String strEndpoint = "rest://localhost:8080"; TcpClientConnection tcpClientConnection; Map requestMap; Queue writeQueue; Queue packageQueue; @Before public void setup() { tcpClientConnection = new TcpClientConnection(context, netClientWrapper, strEndpoint); requestMap = Deencapsulation.getField(tcpClientConnection, "requestMap"); packageQueue = Deencapsulation.getField(tcpClientConnection, "packageQueue"); writeQueue = Deencapsulation.getField(tcpClientConnection, "writeQueue"); } @Test public void localSupportLogin() { Assertions.assertFalse(tcpClientConnection.isLocalSupportLogin()); tcpClientConnection.setLocalSupportLogin(true); Assertions.assertTrue(tcpClientConnection.isLocalSupportLogin()); } @Test public void createLogin() { Assertions.assertNull(tcpClientConnection.createLogin()); } @Test public void onLoginResponse_buffer() { Assertions.assertTrue(tcpClientConnection.onLoginResponse(null)); } @Test public void send_inWorkingStatus(@Mocked AbstractTcpClientPackage tcpClientPackage, @Mocked TcpOutputStream tcpOutputStream) { Deencapsulation.setField(tcpClientConnection, "status", Status.WORKING); long msgId = 1; Buffer byteBuf = Buffer.buffer(); new Expectations(tcpClientConnection) { { tcpClientPackage.getMsgId(); result = msgId; tcpClientPackage.createStream(); result = tcpOutputStream; tcpOutputStream.getBuffer(); result = byteBuf; } }; new MockUp(context) { @Mock void runOnContext(Handler action) { } }; tcpClientConnection.send(tcpClientPackage, ar -> { }); Assertions.assertSame(byteBuf, writeQueue.poll()); Assertions.assertNull(writeQueue.poll()); Assertions.assertEquals(Status.WORKING, Deencapsulation.getField(tcpClientConnection, "status")); } @Test public void send_inDisconnectedStatus(@Mocked AbstractTcpClientPackage tcpClientPackage, @Mocked TcpOutputStream tcpOutputStream) { long msgId = 1; new Expectations(tcpClientConnection) { { tcpClientPackage.getMsgId(); result = msgId; } }; new MockUp(context) { @Mock void runOnContext(Handler action) { action.handle(null); } }; tcpClientConnection.send(tcpClientPackage, ar -> { }); Assertions.assertSame(tcpClientPackage, packageQueue.poll()); Assertions.assertNull(packageQueue.poll()); Assertions.assertEquals(Status.CONNECTING, Deencapsulation.getField(tcpClientConnection, "status")); } @Test public void send_disconnectedToTryLogin(@Mocked AbstractTcpClientPackage tcpClientPackage, @Mocked TcpOutputStream tcpOutputStream) { long msgId = 1; new Expectations(tcpClientConnection) { { tcpClientPackage.getMsgId(); result = msgId; } }; new MockUp(context) { @Mock void runOnContext(Handler action) { Deencapsulation.setField(tcpClientConnection, "status", Status.TRY_LOGIN); action.handle(null); } }; tcpClientConnection.send(tcpClientPackage, ar -> { }); Assertions.assertSame(tcpClientPackage, packageQueue.poll()); Assertions.assertNull(packageQueue.poll()); Assertions.assertEquals(Status.TRY_LOGIN, Deencapsulation.getField(tcpClientConnection, "status")); } @Test public void send_disconnectedToWorking(@Mocked AbstractTcpClientPackage tcpClientPackage, @Mocked TcpOutputStream tcpOutputStream) { long msgId = 1; new Expectations(tcpClientConnection) { { tcpClientPackage.getMsgId(); result = msgId; tcpClientConnection.write((Buffer) any); } }; new MockUp(context) { @Mock void runOnContext(Handler action) { Deencapsulation.setField(tcpClientConnection, "status", Status.WORKING); action.handle(null); } }; tcpClientConnection.send(tcpClientPackage, ar -> { }); Assertions.assertNull(writeQueue.poll()); Assertions.assertNull(packageQueue.poll()); Assertions.assertEquals(Status.WORKING, Deencapsulation.getField(tcpClientConnection, "status")); } @Test public void connect_success(@Mocked NetSocketImpl netSocket) { Promise promise = Promise.promise(); new MockUp(netClientWrapper) { @Mock public Future connect(boolean ssl, int port, String host) { promise.complete(netSocket); return promise.future(); } }; tcpClientConnection.connect(); Assertions.assertSame(netSocket, tcpClientConnection.getNetSocket()); Assertions.assertEquals(Status.WORKING, Deencapsulation.getField(tcpClientConnection, "status")); } @Test public void connect_failed() { requestMap.put(10L, new TcpRequest(10, ar -> { })); Promise promise = Promise.promise(); RuntimeException error = new RuntimeExceptionWithoutStackTrace(); new MockUp(netClientWrapper) { @Mock public Future connect(boolean ssl, int port, String host) { promise.fail(error); return promise.future(); } }; tcpClientConnection.connect(); Assertions.assertEquals(Status.DISCONNECTED, Deencapsulation.getField(tcpClientConnection, "status")); Assertions.assertEquals(0, requestMap.size()); } @Test public void onClosed(@Mocked NetSocketImpl netSocket) { requestMap.put(10L, new TcpRequest(10, ar -> { })); tcpClientConnection.initNetSocket(netSocket); tcpClientConnection.onClosed(null); Assertions.assertEquals(Status.DISCONNECTED, Deencapsulation.getField(tcpClientConnection, "status")); Assertions.assertEquals(0, requestMap.size()); } @Test public void onReply_notExist() { // should not throw exception tcpClientConnection.onReply(1, null, null); } @Test public void on_exist() { long msgId = 1L; AtomicInteger count = new AtomicInteger(); requestMap.put(msgId, new TcpRequest(10, ar -> count.incrementAndGet())); tcpClientConnection.onReply(msgId, null, null); Assertions.assertEquals(1, count.get()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.util.Collections; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.jupiter.api.Assertions; import jakarta.servlet.http.HttpServletRequest; public class TestAbstractHttpServletRequest { HttpServletRequest request = new AbstractHttpServletRequest() { }; @Test public void testAttribute() { String key = "a1"; String value = "abc"; request.setAttribute(key, value); Assertions.assertSame(value, request.getAttribute(key)); MatcherAssert.assertThat(Collections.list(request.getAttributeNames()), Matchers.contains(key)); request.setAttribute("a2", "v"); MatcherAssert.assertThat(Collections.list(request.getAttributeNames()), Matchers.containsInAnyOrder(key, "a2")); request.removeAttribute(key); Assertions.assertNull(request.getAttribute(key)); } private void checkError(Error error) { Assertions.assertEquals("not supported method", error.getMessage()); } @Test public void testGetCharacterEncoding() { Error error = Assertions.assertThrows(Error.class, () -> request.getCharacterEncoding()); checkError(error); } @Test public void testSetCharacterEncoding() { Error error = Assertions.assertThrows(Error.class, () -> request.setCharacterEncoding("")); checkError(error); } @Test public void testGetContentLength() { Error error = Assertions.assertThrows(Error.class, () -> request.getContentLength()); checkError(error); } @Test public void testGetContentLengthLong() { Error error = Assertions.assertThrows(Error.class, () -> request.getContentLengthLong()); checkError(error); } @Test public void testGetContentType() { Error error = Assertions.assertThrows(Error.class, () -> request.getContentType()); checkError(error); } @Test public void testGetInputStream() { Error error = Assertions.assertThrows(Error.class, () -> request.getInputStream()); checkError(error); } @Test public void testGetParameter() { Error error = Assertions.assertThrows(Error.class, () -> request.getParameter("")); checkError(error); } @Test public void testGetParameterNames() { Error error = Assertions.assertThrows(Error.class, () -> request.getParameterNames()); checkError(error); } @Test public void testGetParameterValues() { Error error = Assertions.assertThrows(Error.class, () -> request.getParameterValues("")); checkError(error); } @Test public void testGetParameterMap() { Error error = Assertions.assertThrows(Error.class, () -> request.getParameterMap()); checkError(error); } @Test public void testGetProtocol() { Error error = Assertions.assertThrows(Error.class, () -> request.getProtocol()); checkError(error); } @Test public void testGetScheme() { Error error = Assertions.assertThrows(Error.class, () -> request.getScheme()); checkError(error); } @Test public void testGetServerName() { Error error = Assertions.assertThrows(Error.class, () -> request.getServerName()); checkError(error); } @Test public void testGetServerPort() { Error error = Assertions.assertThrows(Error.class, () -> request.getServerPort()); checkError(error); } @Test public void testGetReader() { Error error = Assertions.assertThrows(Error.class, () -> request.getReader()); checkError(error); } @Test public void testGetRemoteAddr() { Error error = Assertions.assertThrows(Error.class, () -> request.getRemoteAddr()); checkError(error); } @Test public void testGetRemoteHost() { Error error = Assertions.assertThrows(Error.class, () -> request.getRemoteHost()); checkError(error); } @Test public void testGetLocale() { Error error = Assertions.assertThrows(Error.class, () -> request.getLocale()); checkError(error); } @Test public void testGetLocales() { Error error = Assertions.assertThrows(Error.class, () -> request.getLocales()); checkError(error); } @Test public void testIsSecure() { Error error = Assertions.assertThrows(Error.class, () -> request.isSecure()); checkError(error); } @Test public void testGetRequestDispatcher() { Error error = Assertions.assertThrows(Error.class, () -> request.getRequestDispatcher("")); checkError(error); } @Test public void testGetRemotePort() { Error error = Assertions.assertThrows(Error.class, () -> request.getRemotePort()); checkError(error); } @Test public void testGetLocalName() { Error error = Assertions.assertThrows(Error.class, () -> request.getLocalName()); checkError(error); } @Test public void testGetLocalAddr() { Error error = Assertions.assertThrows(Error.class, () -> request.getLocalAddr()); checkError(error); } @Test public void testGetLocalPort() { Error error = Assertions.assertThrows(Error.class, () -> request.getLocalPort()); checkError(error); } @Test public void testGetServletContext() { Error error = Assertions.assertThrows(Error.class, () -> request.getServletContext()); checkError(error); } @Test public void testStartAsync() { Error error = Assertions.assertThrows(Error.class, () -> request.startAsync()); checkError(error); } @Test public void testStartAsyncWithParam() { Error error = Assertions.assertThrows(Error.class, () -> request.startAsync(null, null)); checkError(error); } @Test public void testIsAsyncStarted() { Error error = Assertions.assertThrows(Error.class, () -> request.isAsyncStarted()); checkError(error); } @Test public void testIsAsyncSupported() { Error error = Assertions.assertThrows(Error.class, () -> request.isAsyncSupported()); checkError(error); } @Test public void testGetAsyncContext() { Error error = Assertions.assertThrows(Error.class, () -> request.getAsyncContext()); checkError(error); } @Test public void testGetDispatcherType() { Error error = Assertions.assertThrows(Error.class, () -> request.getDispatcherType()); checkError(error); } @Test public void testGetAuthType() { Error error = Assertions.assertThrows(Error.class, () -> request.getAuthType()); checkError(error); } @Test public void testGetCookies() { Error error = Assertions.assertThrows(Error.class, () -> request.getCookies()); checkError(error); } @Test public void testGetDateHeader() { Error error = Assertions.assertThrows(Error.class, () -> request.getDateHeader("")); checkError(error); } @Test public void testGetHeader() { Error error = Assertions.assertThrows(Error.class, () -> request.getHeader("")); checkError(error); } @Test public void testGetHeaders() { Error error = Assertions.assertThrows(Error.class, () -> request.getHeaders("")); checkError(error); } @Test public void testGetHeaderNames() { Error error = Assertions.assertThrows(Error.class, () -> request.getHeaderNames()); checkError(error); } @Test public void testGetIntHeader() { Error error = Assertions.assertThrows(Error.class, () -> request.getIntHeader("")); checkError(error); } @Test public void testGetMethod() { Error error = Assertions.assertThrows(Error.class, () -> request.getMethod()); checkError(error); } @Test public void testGetPathInfo() { Error error = Assertions.assertThrows(Error.class, () -> request.getPathInfo()); checkError(error); } @Test public void testGetPathTranslated() { Error error = Assertions.assertThrows(Error.class, () -> request.getPathTranslated()); checkError(error); } @Test public void testGetContextPath() { Error error = Assertions.assertThrows(Error.class, () -> request.getContextPath()); checkError(error); } @Test public void testGetQueryString() { Error error = Assertions.assertThrows(Error.class, () -> request.getQueryString()); checkError(error); } @Test public void testGetRemoteUser() { Error error = Assertions.assertThrows(Error.class, () -> request.getRemoteUser()); checkError(error); } @Test public void testIsUserInRole() { Error error = Assertions.assertThrows(Error.class, () -> request.isUserInRole("")); checkError(error); } @Test public void testGetUserPrincipal() { Error error = Assertions.assertThrows(Error.class, () -> request.getUserPrincipal()); checkError(error); } @Test public void testGetRequestedSessionId() { Error error = Assertions.assertThrows(Error.class, () -> request.getRequestedSessionId()); checkError(error); } @Test public void testGetRequestURI() { Error error = Assertions.assertThrows(Error.class, () -> request.getRequestURI()); checkError(error); } @Test public void testGetRequestURL() { Error error = Assertions.assertThrows(Error.class, () -> request.getRequestURL()); checkError(error); } @Test public void testGetServletPath() { Error error = Assertions.assertThrows(Error.class, () -> request.getServletPath()); checkError(error); } @Test public void testGetSessionWithParam() { Error error = Assertions.assertThrows(Error.class, () -> request.getSession(true)); checkError(error); } @Test public void testGetSession() { Error error = Assertions.assertThrows(Error.class, () -> request.getSession()); checkError(error); } @Test public void testChangeSessionId() { Error error = Assertions.assertThrows(Error.class, () -> request.changeSessionId()); checkError(error); } @Test public void testIsRequestedSessionIdValid() { Error error = Assertions.assertThrows(Error.class, () -> request.isRequestedSessionIdValid()); checkError(error); } @Test public void testIsRequestedSessionIdFromCookie() { Error error = Assertions.assertThrows(Error.class, () -> request.isRequestedSessionIdFromCookie()); checkError(error); } @Test public void testIsRequestedSessionIdFromURL() { Error error = Assertions.assertThrows(Error.class, () -> request.isRequestedSessionIdFromURL()); checkError(error); } @Test public void testAuthenticate() { Error error = Assertions.assertThrows(Error.class, () -> request.authenticate(null)); checkError(error); } @Test public void testLogin() { Error error = Assertions.assertThrows(Error.class, () -> request.login(null, null)); checkError(error); } @Test public void testLogout() { Error error = Assertions.assertThrows(Error.class, () -> request.logout()); checkError(error); } @Test public void testGetParts() { Error error = Assertions.assertThrows(Error.class, () -> request.getParts()); checkError(error); } @Test public void testGetPart() { Error error = Assertions.assertThrows(Error.class, () -> request.getPart("")); checkError(error); } @Test public void testUpgrade() { Error error = Assertions.assertThrows(Error.class, () -> request.upgrade(null)); checkError(error); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractHttpServletResponse { HttpServletResponseEx response = new AbstractHttpServletResponse() { }; private void checkError(Error error) { Assertions.assertEquals("not supported method", error.getMessage()); } @Test public void testGetCharacterEncoding() { Error error = Assertions.assertThrows(Error.class, () -> response.getCharacterEncoding()); checkError(error); } @Test public void testGetContentType() { Error error = Assertions.assertThrows(Error.class, () -> response.getContentType()); checkError(error); } @Test public void testGetOutputStream() { Error error = Assertions.assertThrows(Error.class, () -> response.getOutputStream()); checkError(error); } @Test public void testGetWriter() { Error error = Assertions.assertThrows(Error.class, () -> response.getWriter()); checkError(error); } @Test public void testSetCharacterEncoding() { Error error = Assertions.assertThrows(Error.class, () -> response.setCharacterEncoding("")); checkError(error); } @Test public void testSetContentLength() { Error error = Assertions.assertThrows(Error.class, () -> response.setContentLength(0)); checkError(error); } @Test public void testSetContentLengthLong() { Error error = Assertions.assertThrows(Error.class, () -> response.setContentLengthLong(0)); checkError(error); } @Test public void testSetContentType() { Error error = Assertions.assertThrows(Error.class, () -> response.setContentType("")); checkError(error); } @Test public void testSetBufferSize() { Error error = Assertions.assertThrows(Error.class, () -> response.setBufferSize(0)); checkError(error); } @Test public void testGetBufferSize() { Error error = Assertions.assertThrows(Error.class, () -> response.getBufferSize()); checkError(error); } @Test public void testFlushBuffer() { Assertions.assertDoesNotThrow(() -> response.flushBuffer()); } @Test public void testResetBuffer() { Error error = Assertions.assertThrows(Error.class, () -> response.resetBuffer()); checkError(error); } @Test public void testIsCommitted() { Error error = Assertions.assertThrows(Error.class, () -> response.isCommitted()); checkError(error); } @Test public void testReset() { Error error = Assertions.assertThrows(Error.class, () -> response.reset()); checkError(error); } @Test public void testSetLocale() { Error error = Assertions.assertThrows(Error.class, () -> response.setLocale(null)); checkError(error); } @Test public void testGetLocale() { Error error = Assertions.assertThrows(Error.class, () -> response.getLocale()); checkError(error); } @Test public void testAddCookie() { Error error = Assertions.assertThrows(Error.class, () -> response.addCookie(null)); checkError(error); } @Test public void testContainsHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.containsHeader(null)); checkError(error); } @Test public void testEncodeURL() { Error error = Assertions.assertThrows(Error.class, () -> response.encodeURL(null)); checkError(error); } @Test public void testEncodeRedirectURL() { Error error = Assertions.assertThrows(Error.class, () -> response.encodeRedirectURL(null)); checkError(error); } @Test public void testSendErrorScAndMsg() { Error error = Assertions.assertThrows(Error.class, () -> response.sendError(0, null)); checkError(error); } @Test public void testSendErrorSc() { Error error = Assertions.assertThrows(Error.class, () -> response.sendError(0)); checkError(error); } @Test public void testSendRedirect() { Error error = Assertions.assertThrows(Error.class, () -> response.sendRedirect(null)); checkError(error); } @Test public void testSetDateHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.setDateHeader(null, 0)); checkError(error); } @Test public void testAddDateHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.addDateHeader(null, 0)); checkError(error); } @Test public void testSetHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.setHeader(null, null)); checkError(error); } @Test public void testAddHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.addHeader(null, null)); checkError(error); } @Test public void testSetIntHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.setIntHeader(null, 0)); checkError(error); } @Test public void testAddIntHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.addIntHeader(null, 0)); checkError(error); } @Test public void testSetStatusSc() { Error error = Assertions.assertThrows(Error.class, () -> response.setStatus(0)); checkError(error); } @Test public void testGetStatus() { Error error = Assertions.assertThrows(Error.class, () -> response.getStatus()); checkError(error); } @Test public void testGetHeader() { Error error = Assertions.assertThrows(Error.class, () -> response.getHeader("")); checkError(error); } @Test public void testGetHeaders() { Error error = Assertions.assertThrows(Error.class, () -> response.getHeaders("")); checkError(error); } @Test public void testGetHeaderNames() { Error error = Assertions.assertThrows(Error.class, () -> response.getHeaderNames()); checkError(error); } @Test public void testGetStatusType() { Error error = Assertions.assertThrows(Error.class, () -> response.getStatusType()); checkError(error); } @Test public void attribute() { response.setAttribute("k", "v"); Assertions.assertEquals("v", response.getAttribute("k")); } @Test public void sendPart() { Error error = Assertions.assertThrows(Error.class, () -> response.sendPart(null)); checkError(error); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestBodyBufferSupportImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import io.vertx.core.buffer.Buffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBodyBufferSupportImpl { BodyBufferSupportImpl impl = new BodyBufferSupportImpl(); @Test public void testSetBodyBuffer() { impl.setBodyBytes(new byte[] {}); impl.setBodyLength(10); Assertions.assertNotNull(impl.getBodyBytes()); Assertions.assertEquals(10, impl.getBodyBytesLength()); impl.setBodyBuffer(null); Assertions.assertNull(impl.getBodyBytes()); Assertions.assertEquals(0, impl.getBodyBytesLength()); } @Test public void testGetBodyBuffer() { Assertions.assertNull(impl.getBodyBuffer()); Buffer bodyBuffer = Buffer.buffer(); impl.setBodyBuffer(bodyBuffer); Assertions.assertSame(bodyBuffer, impl.getBodyBuffer()); } @Test public void testGetBodyBytes() { Assertions.assertNull(impl.getBodyBytes()); byte[] bytes = new byte[] {1, 2, 3}; Buffer bodyBuffer = Buffer.buffer(bytes); impl.setBodyBuffer(bodyBuffer); Assertions.assertArrayEquals(bytes, impl.getBodyBytes()); } @Test public void testGetBodyBytesLength() { Assertions.assertEquals(0, impl.getBodyBytesLength()); byte[] bytes = new byte[] {1, 2, 3}; Buffer bodyBuffer = Buffer.buffer(bytes); impl.setBodyBuffer(bodyBuffer); Assertions.assertEquals(3, impl.getBodyBytesLength()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import io.vertx.ext.web.FileUpload; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestFileUploadPart { FileUpload fileUpload; FileUploadPart part; static File file; static String content = "fileContent"; @BeforeAll public static void classSetup() throws IOException { file = Files.createTempFile("upload", ".txt").toFile(); file.deleteOnExit(); FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8, false); } @BeforeEach public void setup() { fileUpload = Mockito.mock(FileUpload.class); part = new FileUploadPart(fileUpload); } @AfterEach public void after() { Mockito.reset(fileUpload); } @Test public void getInputStream() throws IOException { Mockito.when(fileUpload.uploadedFileName()).thenReturn(file.getAbsolutePath()); try (InputStream is = part.getInputStream()) { Assertions.assertEquals(content, IOUtils.toString(is, StandardCharsets.UTF_8)); } } @Test public void getContentType() { String contentType = "type"; Mockito.when(fileUpload.contentType()).thenReturn(contentType); Assertions.assertEquals(contentType, part.getContentType()); } @Test public void getName() { String name = "pName"; Mockito.when(fileUpload.name()).thenReturn(name); Assertions.assertEquals(name, part.getName()); } @Test public void getSubmittedFileName() { String clientName = "clientName"; Mockito.when(fileUpload.fileName()).thenReturn(clientName); Assertions.assertEquals(clientName, part.getSubmittedFileName()); } @Test public void getSize() { long fileSize = 10; Mockito.when(fileUpload.size()).thenReturn(fileSize); Assertions.assertEquals(fileSize, part.getSize()); } @Test public void write() throws IOException { Mockito.when(fileUpload.uploadedFileName()).thenReturn(file.getAbsolutePath()); File targetFile = new File(UUID.randomUUID().toString()); targetFile.deleteOnExit(); part.write(targetFile.getAbsolutePath()); Assertions.assertEquals(content, FileUtils.readFileToString(targetFile, StandardCharsets.UTF_8)); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.core.MediaType; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import io.vertx.core.buffer.Buffer; import mockit.Deencapsulation; import mockit.Expectations; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestStandardHttpServletRequestEx { @Mocked HttpServletRequest request; StandardHttpServletRequestEx requestEx; @Before public void setup() { requestEx = new StandardHttpServletRequestEx(request); } @Test public void setBodyBuffer() { Buffer bodyBuffer = Buffer.buffer(); bodyBuffer.appendString("abc"); requestEx.setBodyBuffer(bodyBuffer); Assertions.assertSame(bodyBuffer, requestEx.getBodyBuffer()); Assertions.assertArrayEquals("abc".getBytes(), Arrays.copyOf(requestEx.getBodyBytes(), requestEx.getBodyBytesLength())); } @Test public void getInputStreamNotCache() throws IOException { ServletInputStream inputStream = request.getInputStream(); Assertions.assertSame(inputStream, requestEx.getInputStream()); } @Test public void getInputStreamCache() throws IOException { requestEx.setCacheRequest(true); ServletInputStream inputStream = request.getInputStream(); new Expectations(IOUtils.class) { { IOUtils.toByteArray(inputStream); result = "abc".getBytes(); } }; ServletInputStream cachedInputStream = requestEx.getInputStream(); Assertions.assertEquals("abc", IOUtils.toString(cachedInputStream, StandardCharsets.UTF_8)); Assertions.assertEquals("abc", requestEx.getBodyBuffer().toString()); // do not create another one Assertions.assertSame(cachedInputStream, requestEx.getInputStream()); } @Test public void parameterMap_inherited() { Map inherited = new HashMap<>(); String[] v1 = new String[] {"v1-1", "v1-2"}; inherited.put("p1", v1); new Expectations() { { request.getParameterMap(); result = inherited; request.getMethod(); result = HttpMethod.POST; } }; Assertions.assertSame(inherited, requestEx.getParameterMap()); MatcherAssert.assertThat(Collections.list(requestEx.getParameterNames()), Matchers.contains("p1")); Assertions.assertSame(v1, requestEx.getParameterValues("p1")); Assertions.assertEquals("v1-1", requestEx.getParameter("p1")); } @Test public void parameterMap_merge() throws IOException { Map inherited = new HashMap<>(); String[] v1 = new String[] {"v1-1", "v1-2"}; inherited.put("p1", v1); Buffer buffer = Buffer.buffer("p1=v1-3;p2=v2"); BufferInputStream inputStream = new BufferInputStream(buffer); new Expectations() { { request.getParameterMap(); result = inherited; request.getMethod(); result = HttpMethod.PUT; request.getContentType(); result = MediaType.APPLICATION_FORM_URLENCODED.toUpperCase(Locale.US) + ";abc"; request.getInputStream(); result = inputStream; } }; MatcherAssert.assertThat(Collections.list(requestEx.getParameterNames()), Matchers.containsInAnyOrder("p1", "p2")); MatcherAssert.assertThat(requestEx.getParameterValues("p1"), Matchers.arrayContaining("v1-1", "v1-2", "v1-3")); Assertions.assertEquals("v1-1", requestEx.getParameter("p1")); } @Test public void setParameter() { Map parameterMap = new HashMap<>(); Deencapsulation.setField(requestEx, "parameterMap", parameterMap); requestEx.setParameter("k1", "v1"); requestEx.setParameter("k2", "v2"); Assertions.assertEquals("v1", requestEx.getParameter("k1")); Assertions.assertEquals("v2", requestEx.getParameter("k2")); Assertions.assertSame(parameterMap, requestEx.getParameterMap()); MatcherAssert.assertThat(Collections.list(requestEx.getParameterNames()), Matchers.containsInAnyOrder("k1", "k2")); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.lang3.RandomStringUtils; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import io.vertx.core.buffer.Buffer; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.Part; public class TestStandardHttpServletResponseEx { HttpServletResponse response; StandardHttpServletResponseEx responseEx; @Before public void setup() { response = Mockito.mock(HttpServletResponse.class); responseEx = new StandardHttpServletResponseEx(response); } @Test public void setBodyBuffer() { Assertions.assertNull(responseEx.getBodyBuffer()); Buffer bodyBuffer = Buffer.buffer(); bodyBuffer.appendString("abc"); responseEx.setBodyBuffer(bodyBuffer); Assertions.assertEquals("abc", responseEx.getBodyBuffer().toString()); } @Test public void getBodyBytes() { Buffer bodyBuffer = Buffer.buffer(); bodyBuffer.appendString("abc"); responseEx.setBodyBuffer(bodyBuffer); Assertions.assertEquals("abc", new String(responseEx.getBodyBytes(), 0, responseEx.getBodyBytesLength())); } @Test public void getBodyBytesLength() { Buffer bodyBuffer = Buffer.buffer(); bodyBuffer.appendString("abc"); responseEx.setBodyBuffer(bodyBuffer); Assertions.assertEquals(3, responseEx.getBodyBytesLength()); } @Test public void flushBuffer() throws IOException { Buffer buffer = Buffer.buffer(); ServletOutputStream output = new ServletOutputStream() { @Override public boolean isReady() { return true; } @Override public void setWriteListener(WriteListener writeListener) { } @Override public void write(int b) { buffer.appendByte((byte) b); } }; Mockito.when(response.getOutputStream()).thenReturn(output); responseEx = new StandardHttpServletResponseEx(response); // no body responseEx.flushBuffer(); Assertions.assertEquals(0, buffer.length()); Buffer body = Buffer.buffer().appendString("body"); responseEx.setBodyBuffer(body); responseEx.flushBuffer(); Assertions.assertEquals(0, buffer.length()); } @Test public void attribute() { responseEx.setAttribute("k", "v"); Assertions.assertEquals("v", responseEx.getAttribute("k")); } @Test public void sendPart_succ() throws Throwable { String src = RandomStringUtils.random(100, true, true); InputStream inputStream = new ByteArrayInputStream(src.getBytes()); Part part = new InputStreamPart("name", inputStream); Buffer buffer = Buffer.buffer(); ServletOutputStream outputStream = new ServletOutputStream() { @Override public boolean isReady() { return false; } @Override public void setWriteListener(WriteListener writeListener) { } @Override public void write(int b) { buffer.appendByte((byte) b); } }; Mockito.when(response.getOutputStream()).thenReturn(outputStream); responseEx.sendPart(part).get(); Assertions.assertEquals(src, buffer.toString()); } @Test public void sendPart_failed() throws Throwable { Part part = Mockito.mock(Part.class); RuntimeException error = new RuntimeExceptionWithoutStackTrace(); Mockito.when(response.getOutputStream()).thenThrow(error); Assertions.assertThrows(RuntimeException.class, () -> responseEx.sendPart(part).get()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientRequestToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.util.Collections; import jakarta.ws.rs.core.HttpHeaders; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpMethod; import mockit.Expectations; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestVertxClientRequestToHttpServletRequest { HttpClientRequest clientRequest; Buffer bodyBuffer = Buffer.buffer(); VertxClientRequestToHttpServletRequest request; @BeforeEach public void setup() { clientRequest = Mockito.mock(HttpClientRequest.class); request = new VertxClientRequestToHttpServletRequest(clientRequest, bodyBuffer); } @AfterEach public void after() { Mockito.reset(clientRequest); } @Test public void testGetRequestURI() { Mockito.when(clientRequest.path()).thenReturn("/path"); Assertions.assertEquals("/path", request.getRequestURI()); } @Test public void testGetQueryString() { Mockito.when(clientRequest.query()).thenReturn("a=1&b=2"); Assertions.assertEquals("a=1&b=2", request.getQueryString()); } @Test public void testGetHeader() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("name", "value"); Mockito.when(clientRequest.headers()).thenReturn(headers); Assertions.assertEquals("value", request.getHeader("name")); } @Test public void testGetHeaders() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("name", "value"); Mockito.when(clientRequest.headers()).thenReturn(headers); MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("value")); } @Test public void testGetHeaderNames() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("name", "value"); Mockito.when(clientRequest.headers()).thenReturn(headers); MatcherAssert.assertThat(Collections.list(request.getHeaderNames()), Matchers.contains("name")); } @Test public void testSetHeader() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); Mockito.when(clientRequest.headers()).thenReturn(headers); request.setHeader("name", "v1"); request.setHeader("name", "v2"); MatcherAssert.assertThat(headers.getAll("name"), Matchers.contains("v2")); } @Test public void testAddHeader() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); Mockito.when(clientRequest.headers()).thenReturn(headers); request.addHeader("name", "v1"); request.addHeader("name", "v2"); MatcherAssert.assertThat(headers.getAll("name"), Matchers.contains("v1", "v2")); } @Test public void testGetContextPath() { Assertions.assertEquals("", request.getContextPath()); } @Test public void getMethod() { Mockito.when(clientRequest.getMethod()).thenReturn(HttpMethod.GET); Assertions.assertEquals("GET", request.getMethod()); } @Test public void getContentType() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); Mockito.when(clientRequest.headers()).thenReturn(headers); request.addHeader(HttpHeaders.CONTENT_TYPE, "ct"); Assertions.assertEquals("ct", request.getContentType()); } @Test public void getCharacterEncoding() { String contentType = "ct"; String characterEncoding = "ce"; MultiMap headers = MultiMap.caseInsensitiveMultiMap(); new Expectations(HttpUtils.class) { { HttpUtils.getCharsetFromContentType(contentType); result = characterEncoding; } }; Mockito.when(clientRequest.headers()).thenReturn(headers); request.addHeader(HttpHeaders.CONTENT_TYPE, contentType); Assertions.assertEquals("ce", request.getCharacterEncoding()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientResponseToHttpServletResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response.StatusType; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientResponse; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestVertxClientResponseToHttpServletResponse { HttpClientResponse clientResponse; Buffer bodyBuffer = Buffer.buffer(); VertxClientResponseToHttpServletResponse response; @BeforeEach public void setup() { clientResponse = Mockito.mock(HttpClientResponse.class); response = new VertxClientResponseToHttpServletResponse(clientResponse, bodyBuffer); } @AfterEach public void after() { Mockito.reset(clientResponse); } @Test public void getStatus() { Mockito.when(clientResponse.statusCode()).thenReturn(123); Assertions.assertEquals(123, response.getStatus()); } @Test public void getStatusType() { Mockito.when(clientResponse.statusCode()).thenReturn(123); Mockito.when(clientResponse.statusMessage()).thenReturn("test"); StatusType type = response.getStatusType(); Assertions.assertSame(type, response.getStatusType()); Assertions.assertEquals(123, type.getStatusCode()); Assertions.assertEquals("test", type.getReasonPhrase()); } @Test public void getContentType() { Mockito.when(clientResponse.getHeader(HttpHeaders.CONTENT_TYPE)).thenReturn("json"); Assertions.assertEquals("json", response.getContentType()); } @Test public void getHeader() { Mockito.when(clientResponse.getHeader("name")).thenReturn("value"); Assertions.assertEquals("value", response.getHeader("name")); } @Test public void getHeaders() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("name", "v1"); headers.add("name", "v2"); Mockito.when(clientResponse.headers()).thenReturn(headers); MatcherAssert.assertThat(response.getHeaders("name"), Matchers.contains("v1", "v2")); } @Test public void getHeaderNames() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("n1", "v1"); headers.add("n2", "v2"); Mockito.when(clientResponse.headers()).thenReturn(headers); MatcherAssert.assertThat(response.getHeaderNames(), Matchers.contains("n1", "n2")); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerRequestToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.http; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import jakarta.servlet.AsyncContext; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.Cookie; import jakarta.ws.rs.core.HttpHeaders; import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.impl.RoutingContextInternal; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.http.HttpUtils; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.RoutingContext; import mockit.Deencapsulation; import mockit.Expectations; import mockit.Mocked; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; public class TestVertxServerRequestToHttpServletRequest { @Mocked RoutingContext context; @Mocked HttpServerRequest vertxRequest; @Mocked SocketAddress socketAddress; RequestBody mockRequestBody; RoutingContextInternal mockContext; HttpServerRequest mockHttpServerRequest; SocketAddress mockSocketAddress; VertxServerRequestToHttpServletRequest request; @Before public void setup() { new Expectations() { { context.request(); result = vertxRequest; vertxRequest.remoteAddress(); result = socketAddress; } }; mockRequestBody = Mockito.mock(RequestBody.class); mockContext = Mockito.mock(RoutingContextInternal.class); mockHttpServerRequest = Mockito.mock(HttpServerRequest.class); mockSocketAddress = Mockito.mock(SocketAddress.class); // init mocks Mockito.when(mockHttpServerRequest.remoteAddress()).thenReturn(mockSocketAddress); Mockito.when(mockContext.body()).thenReturn(mockRequestBody); Mockito.when(mockContext.request()).thenReturn(mockHttpServerRequest); request = new VertxServerRequestToHttpServletRequest(context); } @After public void clean() { Mockito.reset(mockRequestBody); Mockito.reset(mockContext); } @Test public void constructWithPath() { request = new VertxServerRequestToHttpServletRequest(context, "/path"); Assertions.assertEquals("/path", request.getRequestURI()); } @Test public void setBodyBuffer() { Holder bodyHolder = new Holder<>(); Mockito.doAnswer(invocation -> { bodyHolder.value = invocation.getArgument(0); return null; }).when(mockContext).setBody(Mockito.any()); request = new VertxServerRequestToHttpServletRequest(mockContext); Buffer bodyBuffer = Buffer.buffer(); request.setBodyBuffer(bodyBuffer); Assertions.assertSame(bodyBuffer, bodyHolder.value); Assertions.assertSame(bodyBuffer, request.getBodyBuffer()); } @Test public void testGetContentType() { VertxServerRequestToHttpServletRequest request = new VertxServerRequestToHttpServletRequest(mockContext); Mockito.when(mockHttpServerRequest.getHeader(HttpHeaders.CONTENT_TYPE)).thenReturn("json"); Assertions.assertEquals("json", request.getContentType()); } @Test public void testGetCookies() { Set vertxCookies = new HashSet<>(); vertxCookies.add(io.vertx.core.http.Cookie.cookie("c1", "c1v")); vertxCookies.add(io.vertx.core.http.Cookie.cookie("c2", "c2v")); new Expectations() { { context.request().cookies(); result = vertxCookies; } }; Cookie[] cookies = request.getCookies(); // we can't ensure the sequence when set to list if (cookies[0].getName().equals("c1")) { Assertions.assertEquals("c1", cookies[0].getName()); Assertions.assertEquals("c1v", cookies[0].getValue()); Assertions.assertEquals("c2", cookies[1].getName()); Assertions.assertEquals("c2v", cookies[1].getValue()); } else { Assertions.assertEquals("c2", cookies[0].getName()); Assertions.assertEquals("c2v", cookies[0].getValue()); Assertions.assertEquals("c1", cookies[1].getName()); Assertions.assertEquals("c1v", cookies[1].getValue()); } } @Test public void testGetParameter() { new Expectations() { { vertxRequest.getParam("name"); result = "value"; } }; Assertions.assertEquals("value", request.getParameter("name")); } @Test public void testGetParameterValuesNull() { Assertions.assertEquals(0, request.getParameterValues("name").length); } @Test public void testGetParameterValuesNormal() { MultiMap params = MultiMap.caseInsensitiveMultiMap(); params.add("name", "value"); new Expectations() { { vertxRequest.params(); result = params; } }; MatcherAssert.assertThat(request.getParameterValues("name"), Matchers.arrayContaining("value")); } @Test public void testGetParameterMap() { MultiMap params = MultiMap.caseInsensitiveMultiMap(); params.add("name", "value"); new Expectations() { { vertxRequest.params(); result = params; } }; Map result = request.getParameterMap(); MatcherAssert.assertThat(result.keySet(), Matchers.contains("name")); MatcherAssert.assertThat(result.get("name"), Matchers.arrayContaining("value")); Assertions.assertSame(result, request.getParameterMap()); } @Test public void testScheme() { new Expectations() { { vertxRequest.scheme(); result = "abc"; } }; Assertions.assertEquals("abc", request.getScheme()); } @Test public void testGetRemoteAddr() { new Expectations() { { socketAddress.host(); result = "host"; } }; Assertions.assertEquals("host", request.getRemoteAddr()); } @Test public void testGetRemoteAddrNull() { new Expectations() { { socketAddress.host(); result = null; } }; Assertions.assertNull(request.getRemoteAddr()); } @Test public void testGetRemoteHost() { new Expectations() { { socketAddress.host(); result = "host"; } }; Assertions.assertEquals("host", request.getRemoteHost()); } @Test public void testGetRemotePort() { new Expectations() { { socketAddress.port(); result = 1234; } }; Assertions.assertEquals(1234, request.getRemotePort()); } @Test public void testGetgetLocalAddr(@Mocked SocketAddress sa) { new Expectations() { { sa.host(); result = "host"; vertxRequest.localAddress(); result = sa; } }; Assertions.assertEquals("host", request.getLocalAddr()); } @Test public void testGetLocalPort(@Mocked SocketAddress sa) { new Expectations() { { sa.port(); result = 1234; vertxRequest.localAddress(); result = sa; } }; Assertions.assertEquals(1234, request.getLocalPort()); } @Test public void testGetHeader() { new Expectations() { { vertxRequest.getHeader("key"); result = "value"; } }; Assertions.assertEquals("value", request.getHeader("key")); } @Test public void testGetHeaders() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("name", "value"); new Expectations() { { vertxRequest.headers(); result = headers; } }; MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("value")); } @Test public void testGetHeaderNames() { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.add("name", "value"); new Expectations() { { vertxRequest.headers(); result = headers; } }; MatcherAssert.assertThat(Collections.list(request.getHeaderNames()), Matchers.contains("name")); } @Test public void testGetIntHeaderNotExist() { Assertions.assertEquals(-1, request.getIntHeader("key")); } @Test public void testGetIntHeaderNotNumber() { new Expectations() { { vertxRequest.getHeader("key"); result = "value"; } }; try { request.getIntHeader("key"); Assertions.fail("must throw exception"); } catch (NumberFormatException e) { Assertions.assertEquals("For input string: \"value\"", e.getMessage()); } } @Test public void testGetIntHeaderNormal() { new Expectations() { { vertxRequest.getHeader("key"); result = "1"; } }; Assertions.assertEquals(1, request.getIntHeader("key")); } @Test public void testGetMethod() { new Expectations() { { vertxRequest.method(); result = HttpMethod.GET; } }; Assertions.assertEquals("GET", request.getMethod()); } @Test public void testGetPathInfo() { new Expectations() { { vertxRequest.path(); result = "/path"; } }; Assertions.assertEquals("/path", request.getPathInfo()); } @Test public void testGetQueryString() { new Expectations() { { vertxRequest.query(); result = "k1=v1&k2=v2"; } }; Assertions.assertEquals("k1=v1&k2=v2", request.getQueryString()); } @Test public void testGetRequestURI() { new Expectations() { { vertxRequest.path(); result = "/path"; } }; Assertions.assertEquals("/path", request.getRequestURI()); } @Test public void testGetServletPath() { new Expectations() { { vertxRequest.path(); result = "/path"; } }; Assertions.assertEquals("/path", request.getServletPath()); } @Test public void testGetContextPath() { Assertions.assertEquals("", request.getContextPath()); } @Test public void testGetInputStream() throws IOException { Buffer body = Buffer.buffer(); body.appendByte((byte) 1); Mockito.when(mockRequestBody.buffer()).thenReturn(body); Mockito.when(mockContext.request()).thenReturn(vertxRequest); Mockito.when(mockContext.body()).thenReturn(mockRequestBody); VertxServerRequestToHttpServletRequest request = new VertxServerRequestToHttpServletRequest(mockContext); ServletInputStream is1 = request.getInputStream(); Assertions.assertSame(is1, request.getInputStream()); int value = is1.read(); is1.close(); Assertions.assertEquals(1, value); Assertions.assertSame(is1, request.getInputStream()); request.setBodyBuffer(Buffer.buffer().appendByte((byte) 2)); ServletInputStream is2 = request.getInputStream(); Assertions.assertNotSame(is1, is2); } @Test public void testGetAsyncContext() { AsyncContext asyncContext = Deencapsulation.getField(VertxServerRequestToHttpServletRequest.class, "EMPTY_ASYNC_CONTEXT"); Assertions.assertSame(asyncContext, request.getAsyncContext()); } @Test public void getCharacterEncoding() { new Expectations(HttpUtils.class) { { vertxRequest.getHeader(HttpHeaders.CONTENT_TYPE); result = "ct"; HttpUtils.getCharsetFromContentType("ct"); result = "ce"; } }; Assertions.assertEquals("ce", request.getCharacterEncoding()); } @Test public void setParameter() { Map parameterMap = new HashMap<>(); Deencapsulation.setField(request, "parameterMap", parameterMap); request.setParameter("k1", "v1"); request.setParameter("k2", "v2"); Assertions.assertEquals("v1", request.getParameter("k1")); Assertions.assertEquals("v2", request.getParameter("k2")); Assertions.assertSame(parameterMap, request.getParameterMap()); MatcherAssert.assertThat(Collections.list(request.getParameterNames()), Matchers.containsInAnyOrder("k1", "k2")); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpClientMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.net.SocketAddress; import io.vertx.core.net.impl.SocketAddressImpl; import io.vertx.core.spi.observability.HttpRequest; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestDefaultHttpClientMetrics { @Mocked Vertx vertx; VertxOptions vertxOptions = new VertxOptions(); MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); @Mocked HttpClient anyHttpClient; HttpClientOptions options = new HttpClientOptions(); DefaultVertxMetrics defaultVertxMetrics; DefaultHttpClientMetrics clientMetrics_a; DefaultHttpClientMetrics clientMetrics_b; String host = "host"; int port1 = 1; int port2 = 2; SocketAddress address1 = new SocketAddressImpl(port1, host); SocketAddress address2 = new SocketAddressImpl(port2, host); DefaultClientMetrics clientMetrics_a_1; DefaultEndpointMetric endpointMetric_a_1; DefaultTcpSocketMetric socketMetric_a_1; DefaultClientMetrics clientMetrics_a_2; DefaultEndpointMetric endpointMetric_a_2; DefaultTcpSocketMetric socketMetric_a_2; DefaultClientMetrics clientMetrics_b_1; DefaultEndpointMetric endpointMetric_b_1; DefaultTcpSocketMetric socketMetric_b_1; DefaultClientMetrics clientMetrics_b_2; DefaultEndpointMetric endpointMetric_b_2; DefaultTcpSocketMetric socketMetric_b_2; static long nanoTime; @BeforeClass public static void classSetup() { new MockUp() { @Mock long nanoTime() { return nanoTime; } }; } private static DefaultTcpSocketMetric initSocketMetric(DefaultClientMetrics clientMetrics, DefaultHttpClientMetrics metrics, SocketAddress address) { DefaultTcpSocketMetric socketMetric = metrics.connected(address, address.toString()); metrics.endpointConnected(clientMetrics); return socketMetric; } @Before public void setup() { vertxOptions.setMetricsOptions(metricsOptionsEx); defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); defaultVertxMetrics.setVertx(vertx); clientMetrics_a = (DefaultHttpClientMetrics) defaultVertxMetrics.createHttpClientMetrics(options); clientMetrics_b = (DefaultHttpClientMetrics) defaultVertxMetrics.createHttpClientMetrics(options); nanoTime = 1; clientMetrics_a_1 = clientMetrics_a.createEndpointMetrics(address1, 0); socketMetric_a_1 = initSocketMetric(clientMetrics_a_1, clientMetrics_a, address1); endpointMetric_a_1 = socketMetric_a_1.getEndpointMetric(); clientMetrics_a_2 = clientMetrics_a.createEndpointMetrics(address2, 0); socketMetric_a_2 = initSocketMetric(clientMetrics_a_2, clientMetrics_a, address2); endpointMetric_a_2 = socketMetric_a_2.getEndpointMetric(); clientMetrics_b_1 = clientMetrics_b.createEndpointMetrics(address1, 0); socketMetric_b_1 = initSocketMetric(clientMetrics_b_1, clientMetrics_b, address1); endpointMetric_b_1 = socketMetric_b_1.getEndpointMetric(); clientMetrics_b_2 = clientMetrics_b.createEndpointMetrics(address2, 0); socketMetric_b_2 = initSocketMetric(clientMetrics_b_2, clientMetrics_b, address2); endpointMetric_b_2 = socketMetric_b_2.getEndpointMetric(); } @Test public void createMetrics() { Assertions.assertNotSame(clientMetrics_a, clientMetrics_b); } @Test public void createEndpoint() { Assertions.assertSame(endpointMetric_a_1, endpointMetric_b_1); Assertions.assertNotSame(endpointMetric_a_1, endpointMetric_a_2); Assertions.assertNotSame(endpointMetric_a_2, endpointMetric_b_1); Assertions.assertSame(endpointMetric_a_2, endpointMetric_b_2); Assertions.assertEquals(2, endpointMetric_a_1.getCurrentConnectionCount()); Assertions.assertEquals(2, endpointMetric_a_2.getCurrentConnectionCount()); } @Test public void expire() { metricsOptionsEx.setCheckClientEndpointMetricExpiredInNano(10); nanoTime = 2; clientMetrics_a.disconnected(socketMetric_a_1, null); clientMetrics_a.disconnected(socketMetric_a_2, null); nanoTime = 13; defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address1.toString())); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address2.toString())); clientMetrics_b.disconnected(socketMetric_b_1, null); clientMetrics_b.disconnected(socketMetric_b_2, null); nanoTime = 23; defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address1.toString())); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address2.toString())); nanoTime = 24; defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); Assertions .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address1.toString())); Assertions .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address2.toString())); } @Test public void connect() { { Assertions.assertSame(endpointMetric_a_1, socketMetric_a_1.getEndpointMetric()); Assertions.assertTrue(socketMetric_a_1.isConnected()); Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(0, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 2; clientMetrics_a.disconnected(socketMetric_a_1, null); Assertions.assertEquals(2, ((DefaultClientEndpointMetric) endpointMetric_a_1).getLastNanoTime()); Assertions.assertFalse(socketMetric_a_1.isConnected()); Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); } { Assertions.assertSame(endpointMetric_a_2, socketMetric_a_2.getEndpointMetric()); Assertions.assertTrue(socketMetric_a_2.isConnected()); Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(0, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 4; clientMetrics_a.disconnected(socketMetric_a_2, null); Assertions.assertEquals(4, ((DefaultClientEndpointMetric) endpointMetric_a_2).getLastNanoTime()); Assertions.assertFalse(socketMetric_a_2.isConnected()); Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); } { Assertions.assertSame(endpointMetric_b_1, socketMetric_b_1.getEndpointMetric()); Assertions.assertTrue(socketMetric_b_1.isConnected()); Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 6; clientMetrics_b.disconnected(socketMetric_b_1, null); Assertions.assertEquals(6, ((DefaultClientEndpointMetric) endpointMetric_b_1).getLastNanoTime()); Assertions.assertFalse(socketMetric_b_1.isConnected()); Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(0, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); } { Assertions.assertSame(endpointMetric_b_2, socketMetric_b_2.getEndpointMetric()); Assertions.assertTrue(socketMetric_b_2.isConnected()); Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 7; clientMetrics_b.disconnected(socketMetric_b_2, null); Assertions.assertEquals(7, ((DefaultClientEndpointMetric) endpointMetric_b_2).getLastNanoTime()); Assertions.assertFalse(socketMetric_b_2.isConnected()); Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(0, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); } } @Test public void bytesReadAndWritten() { DefaultTcpSocketMetric socketMetric = clientMetrics_a.connected(address1, host); clientMetrics_a.endpointConnected(clientMetrics_a_1); clientMetrics_a.bytesRead(socketMetric, address1, 1); clientMetrics_a.bytesWritten(socketMetric, address1, 1); socketMetric = clientMetrics_a.connected(address2, host); clientMetrics_a.endpointConnected(clientMetrics_a_2); clientMetrics_a.bytesRead(socketMetric, address2, 1); clientMetrics_a.bytesWritten(socketMetric, address2, 1); socketMetric = clientMetrics_b.connected(address1, host); clientMetrics_b.endpointConnected(clientMetrics_b_1); clientMetrics_b.bytesRead(socketMetric, address1, 1); clientMetrics_b.bytesWritten(socketMetric, address1, 1); socketMetric = clientMetrics_b.connected(address2, host); clientMetrics_b.endpointConnected(clientMetrics_b_2); clientMetrics_b.bytesRead(socketMetric, address2, 1); clientMetrics_b.bytesWritten(socketMetric, address2, 1); Assertions.assertEquals(2, endpointMetric_a_1.getBytesRead()); Assertions.assertEquals(2, endpointMetric_a_2.getBytesRead()); Assertions.assertEquals(2, endpointMetric_a_1.getBytesWritten()); Assertions.assertEquals(2, endpointMetric_a_2.getBytesWritten()); } @Test public void requestBegin(@Mocked HttpRequest request) { DefaultTcpSocketMetric socketMetric = clientMetrics_a.connected(address1, host); nanoTime = 2; DefaultRequestMetric requestMetric = clientMetrics_a_1.requestBegin("/ui", request); nanoTime = 3; clientMetrics_a_1.requestEnd(requestMetric); Assertions.assertEquals(2, requestMetric.getRequestBeginTime()); Assertions.assertEquals(3, requestMetric.getRequestEndTime()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpServerMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import java.util.IdentityHashMap; import java.util.Map; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import org.junit.Before; import org.junit.Test; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.net.SocketAddress; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestDefaultHttpServerMetrics { VertxOptions vertxOptions = new VertxOptions(); MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); DefaultVertxMetrics defaultVertxMetrics; @Mocked SocketAddress listen1_addr; @Mocked SocketAddress listen2_addr; @Mocked HttpServerOptions options; @Mocked SocketAddress anyRemoteAddr; DefaultHttpServerMetrics metrics_listen1_server1; DefaultHttpServerMetrics metrics_listen1_server2; DefaultEndpointMetric endpointMetric1; DefaultHttpServerMetrics metrics_listen2_server1; DefaultHttpServerMetrics metrics_listen2_server2; DefaultEndpointMetric endpointMetric2; String remoteName = "remote"; DefaultTcpSocketMetric socketMetric_listen1_1; DefaultTcpSocketMetric socketMetric_listen1_2; DefaultTcpSocketMetric socketMetric_listen2_1; DefaultTcpSocketMetric socketMetric_listen2_2; DefaultTcpSocketMetric socketMetric_listen2_3; @Before public void setup() { vertxOptions.setMetricsOptions(metricsOptionsEx); defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); metrics_listen1_server1 = (DefaultHttpServerMetrics) defaultVertxMetrics .createHttpServerMetrics(options, listen1_addr); metrics_listen1_server2 = (DefaultHttpServerMetrics) defaultVertxMetrics .createHttpServerMetrics(options, listen1_addr); endpointMetric1 = metrics_listen1_server1.getEndpointMetric(); metrics_listen2_server1 = (DefaultHttpServerMetrics) defaultVertxMetrics .createHttpServerMetrics(options, listen2_addr); metrics_listen2_server2 = (DefaultHttpServerMetrics) defaultVertxMetrics .createHttpServerMetrics(options, listen2_addr); endpointMetric2 = metrics_listen2_server1.getEndpointMetric(); socketMetric_listen1_1 = metrics_listen1_server1.connected(anyRemoteAddr, remoteName); socketMetric_listen1_2 = metrics_listen1_server2.connected(anyRemoteAddr, remoteName); socketMetric_listen2_1 = metrics_listen2_server1.connected(anyRemoteAddr, remoteName); socketMetric_listen2_2 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); socketMetric_listen2_3 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); } @Test public void createMetrics() { Map instances = new IdentityHashMap<>(); instances.put(metrics_listen1_server1, null); instances.put(metrics_listen1_server2, null); instances.put(metrics_listen2_server1, null); instances.put(metrics_listen2_server2, null); Assertions.assertEquals(4, instances.size()); Assertions.assertSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen1_server2.getEndpointMetric()); Assertions.assertNotSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen2_server1.getEndpointMetric()); Assertions.assertSame(metrics_listen2_server1.getEndpointMetric(), metrics_listen2_server2.getEndpointMetric()); } @Test public void connectionCount() { Map instances = new IdentityHashMap<>(); instances.put(socketMetric_listen1_1, null); instances.put(socketMetric_listen1_2, null); instances.put(socketMetric_listen2_1, null); instances.put(socketMetric_listen2_2, null); instances.put(socketMetric_listen2_3, null); Assertions.assertEquals(5, instances.size()); Assertions.assertTrue(socketMetric_listen1_1.isConnected()); Assertions.assertTrue(socketMetric_listen1_2.isConnected()); Assertions.assertTrue(socketMetric_listen2_1.isConnected()); Assertions.assertTrue(socketMetric_listen2_2.isConnected()); Assertions.assertTrue(socketMetric_listen2_3.isConnected()); Assertions.assertEquals(2, endpointMetric1.getCurrentConnectionCount()); Assertions.assertEquals(3, endpointMetric2.getCurrentConnectionCount()); // disconnect metrics_listen1_server1.disconnected(socketMetric_listen1_1, anyRemoteAddr); metrics_listen1_server2.disconnected(socketMetric_listen1_2, anyRemoteAddr); metrics_listen2_server1.disconnected(socketMetric_listen2_1, anyRemoteAddr); metrics_listen2_server2.disconnected(socketMetric_listen2_2, anyRemoteAddr); metrics_listen2_server2.disconnected(socketMetric_listen2_3, anyRemoteAddr); Assertions.assertFalse(socketMetric_listen1_1.isConnected()); Assertions.assertFalse(socketMetric_listen1_2.isConnected()); Assertions.assertFalse(socketMetric_listen2_1.isConnected()); Assertions.assertFalse(socketMetric_listen2_2.isConnected()); Assertions.assertFalse(socketMetric_listen2_3.isConnected()); Assertions.assertEquals(0, endpointMetric1.getCurrentConnectionCount()); Assertions.assertEquals(0, endpointMetric2.getCurrentConnectionCount()); } @Test public void bytesRead() { metrics_listen1_server1.bytesRead(socketMetric_listen1_1, anyRemoteAddr, 1); metrics_listen1_server2.bytesRead(socketMetric_listen1_2, anyRemoteAddr, 2); metrics_listen2_server1.bytesRead(socketMetric_listen2_1, anyRemoteAddr, 3); metrics_listen2_server2.bytesRead(socketMetric_listen2_2, anyRemoteAddr, 4); metrics_listen2_server2.bytesRead(socketMetric_listen2_3, anyRemoteAddr, 5); Assertions.assertEquals(3, endpointMetric1.getBytesRead()); Assertions.assertEquals(12, endpointMetric2.getBytesRead()); } @Test public void bytesWritten() { metrics_listen1_server1.bytesWritten(socketMetric_listen1_1, anyRemoteAddr, 1); metrics_listen1_server2.bytesWritten(socketMetric_listen1_2, anyRemoteAddr, 2); metrics_listen2_server1.bytesWritten(socketMetric_listen2_1, anyRemoteAddr, 3); metrics_listen2_server2.bytesWritten(socketMetric_listen2_2, anyRemoteAddr, 4); metrics_listen2_server2.bytesWritten(socketMetric_listen2_3, anyRemoteAddr, 5); Assertions.assertEquals(3, endpointMetric1.getBytesWritten()); Assertions.assertEquals(12, endpointMetric2.getBytesWritten()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpClientMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.SocketAddress; import io.vertx.core.net.impl.SocketAddressImpl; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestDefaultTcpClientMetrics { @Mocked Vertx vertx; VertxOptions vertxOptions = new VertxOptions(); MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); NetClientOptions options = new NetClientOptions(); DefaultVertxMetrics defaultVertxMetrics; DefaultTcpClientMetrics clientMetrics_a; DefaultTcpClientMetrics clientMetrics_b; String host = "host"; int port1 = 1; int port2 = 2; SocketAddress address1 = new SocketAddressImpl(port1, host); SocketAddress address2 = new SocketAddressImpl(port2, host); DefaultEndpointMetric endpointMetric_a_1; DefaultTcpSocketMetric socketMetric_a_1; DefaultEndpointMetric endpointMetric_a_2; DefaultTcpSocketMetric socketMetric_a_2; DefaultEndpointMetric endpointMetric_b_1; DefaultTcpSocketMetric socketMetric_b_1; DefaultEndpointMetric endpointMetric_b_2; DefaultTcpSocketMetric socketMetric_b_2; static long nanoTime; @BeforeClass public static void classSetup() { new MockUp() { @Mock long nanoTime() { return nanoTime; } }; } private static DefaultTcpSocketMetric initSocketMetric(DefaultTcpClientMetrics metrics, SocketAddress address) { return metrics.connected(address, address.toString()); } @Before public void setup() { vertxOptions.setMetricsOptions(metricsOptionsEx); defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); defaultVertxMetrics.setVertx(vertx); clientMetrics_a = (DefaultTcpClientMetrics) defaultVertxMetrics.createNetClientMetrics(options); clientMetrics_b = (DefaultTcpClientMetrics) defaultVertxMetrics.createNetClientMetrics(options); nanoTime = 1; socketMetric_a_1 = initSocketMetric(clientMetrics_a, address1); endpointMetric_a_1 = socketMetric_a_1.getEndpointMetric(); socketMetric_a_2 = initSocketMetric(clientMetrics_a, address2); endpointMetric_a_2 = socketMetric_a_2.getEndpointMetric(); socketMetric_b_1 = initSocketMetric(clientMetrics_b, address1); endpointMetric_b_1 = socketMetric_b_1.getEndpointMetric(); socketMetric_b_2 = initSocketMetric(clientMetrics_b, address2); endpointMetric_b_2 = socketMetric_b_2.getEndpointMetric(); } @Test public void createMetrics() { Assertions.assertNotSame(clientMetrics_a, clientMetrics_b); } @Test public void createEndpoint() { Assertions.assertSame(endpointMetric_a_1, endpointMetric_b_1); Assertions.assertNotSame(endpointMetric_a_1, endpointMetric_a_2); Assertions.assertNotSame(endpointMetric_a_2, endpointMetric_b_1); Assertions.assertSame(endpointMetric_a_2, endpointMetric_b_2); Assertions.assertEquals(2, endpointMetric_a_1.getCurrentConnectionCount()); Assertions.assertEquals(2, endpointMetric_a_2.getCurrentConnectionCount()); } @Test public void expire() { metricsOptionsEx.setCheckClientEndpointMetricExpiredInNano(10); nanoTime = 2; clientMetrics_a.disconnected(socketMetric_a_1, null); clientMetrics_a.disconnected(socketMetric_a_2, null); nanoTime = 13; defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address1.toString())); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address2.toString())); clientMetrics_b.disconnected(socketMetric_b_1, null); clientMetrics_b.disconnected(socketMetric_b_2, null); nanoTime = 23; defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address1.toString())); Assertions.assertNotNull( defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address2.toString())); nanoTime = 24; defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); Assertions .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address1.toString())); Assertions .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address2.toString())); } @Test public void connect() { { Assertions.assertSame(endpointMetric_a_1, socketMetric_a_1.getEndpointMetric()); Assertions.assertTrue(socketMetric_a_1.isConnected()); Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(0, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 2; clientMetrics_a.disconnected(socketMetric_a_1, null); Assertions.assertEquals(2, ((DefaultClientEndpointMetric) endpointMetric_a_1).getLastNanoTime()); Assertions.assertFalse(socketMetric_a_1.isConnected()); Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); } { Assertions.assertSame(endpointMetric_a_2, socketMetric_a_2.getEndpointMetric()); Assertions.assertTrue(socketMetric_a_2.isConnected()); Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(0, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 4; clientMetrics_a.disconnected(socketMetric_a_2, null); Assertions.assertEquals(4, ((DefaultClientEndpointMetric) endpointMetric_a_2).getLastNanoTime()); Assertions.assertFalse(socketMetric_a_2.isConnected()); Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); } { Assertions.assertSame(endpointMetric_b_1, socketMetric_b_1.getEndpointMetric()); Assertions.assertTrue(socketMetric_b_1.isConnected()); Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 6; clientMetrics_b.disconnected(socketMetric_b_1, null); Assertions.assertEquals(6, ((DefaultClientEndpointMetric) endpointMetric_b_1).getLastNanoTime()); Assertions.assertFalse(socketMetric_b_1.isConnected()); Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(0, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); } { Assertions.assertSame(endpointMetric_b_2, socketMetric_b_2.getEndpointMetric()); Assertions.assertTrue(socketMetric_b_2.isConnected()); Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); nanoTime = 7; clientMetrics_b.disconnected(socketMetric_b_2, null); Assertions.assertEquals(7, ((DefaultClientEndpointMetric) endpointMetric_b_2).getLastNanoTime()); Assertions.assertFalse(socketMetric_b_2.isConnected()); Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); Assertions.assertEquals(0, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); } } @Test public void bytesReadAndWritten() { clientMetrics_a.bytesRead(socketMetric_a_1, address1, 1); clientMetrics_a.bytesWritten(socketMetric_a_1, address1, 1); clientMetrics_a.bytesRead(socketMetric_a_2, address2, 1); clientMetrics_a.bytesWritten(socketMetric_a_2, address2, 1); clientMetrics_b.bytesRead(socketMetric_b_1, address1, 1); clientMetrics_b.bytesWritten(socketMetric_b_1, address1, 1); clientMetrics_b.bytesRead(socketMetric_b_2, address2, 1); clientMetrics_b.bytesWritten(socketMetric_b_2, address2, 1); Assertions.assertEquals(2, endpointMetric_a_1.getBytesRead()); Assertions.assertEquals(2, endpointMetric_a_2.getBytesRead()); Assertions.assertEquals(2, endpointMetric_a_1.getBytesWritten()); Assertions.assertEquals(2, endpointMetric_a_2.getBytesWritten()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpServerMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import java.util.IdentityHashMap; import java.util.Map; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; import org.junit.Before; import org.junit.Test; import io.vertx.core.VertxOptions; import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.SocketAddress; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestDefaultTcpServerMetrics { VertxOptions vertxOptions = new VertxOptions(); MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); DefaultVertxMetrics defaultVertxMetrics; @Mocked SocketAddress listen1_addr; @Mocked SocketAddress listen2_addr; @Mocked NetServerOptions options; DefaultTcpServerMetrics metrics_listen1_server1; DefaultTcpServerMetrics metrics_listen1_server2; DefaultEndpointMetric endpointMetric1; DefaultTcpServerMetrics metrics_listen2_server1; DefaultTcpServerMetrics metrics_listen2_server2; DefaultEndpointMetric endpointMetric2; @Mocked SocketAddress anyRemoteAddr; String remoteName = "remote"; DefaultTcpSocketMetric socketMetric_listen1_1; DefaultTcpSocketMetric socketMetric_listen1_2; DefaultTcpSocketMetric socketMetric_listen2_1; DefaultTcpSocketMetric socketMetric_listen2_2; DefaultTcpSocketMetric socketMetric_listen2_3; @Before public void setup() { vertxOptions.setMetricsOptions(metricsOptionsEx); defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); metrics_listen1_server1 = (DefaultTcpServerMetrics) defaultVertxMetrics .createNetServerMetrics(options, listen1_addr); metrics_listen1_server2 = (DefaultTcpServerMetrics) defaultVertxMetrics .createNetServerMetrics(options, listen1_addr); endpointMetric1 = metrics_listen1_server1.getEndpointMetric(); metrics_listen2_server1 = (DefaultTcpServerMetrics) defaultVertxMetrics .createNetServerMetrics(options, listen2_addr); metrics_listen2_server2 = (DefaultTcpServerMetrics) defaultVertxMetrics .createNetServerMetrics(options, listen2_addr); endpointMetric2 = metrics_listen2_server1.getEndpointMetric(); socketMetric_listen1_1 = metrics_listen1_server1.connected(anyRemoteAddr, remoteName); socketMetric_listen1_2 = metrics_listen1_server2.connected(anyRemoteAddr, remoteName); socketMetric_listen2_1 = metrics_listen2_server1.connected(anyRemoteAddr, remoteName); socketMetric_listen2_2 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); socketMetric_listen2_3 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); } @Test public void createMetrics() { Map instances = new IdentityHashMap<>(); instances.put(metrics_listen1_server1, null); instances.put(metrics_listen1_server2, null); instances.put(metrics_listen2_server1, null); instances.put(metrics_listen2_server2, null); Assertions.assertEquals(4, instances.size()); Assertions.assertSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen1_server2.getEndpointMetric()); Assertions.assertNotSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen2_server1.getEndpointMetric()); Assertions.assertSame(metrics_listen2_server1.getEndpointMetric(), metrics_listen2_server2.getEndpointMetric()); } @Test public void connectionCount() { Map instances = new IdentityHashMap<>(); instances.put(socketMetric_listen1_1, null); instances.put(socketMetric_listen1_2, null); instances.put(socketMetric_listen2_1, null); instances.put(socketMetric_listen2_2, null); instances.put(socketMetric_listen2_3, null); Assertions.assertEquals(5, instances.size()); Assertions.assertTrue(socketMetric_listen1_1.isConnected()); Assertions.assertTrue(socketMetric_listen1_2.isConnected()); Assertions.assertTrue(socketMetric_listen2_1.isConnected()); Assertions.assertTrue(socketMetric_listen2_2.isConnected()); Assertions.assertTrue(socketMetric_listen2_3.isConnected()); Assertions.assertEquals(2, endpointMetric1.getCurrentConnectionCount()); Assertions.assertEquals(3, endpointMetric2.getCurrentConnectionCount()); // disconnect metrics_listen1_server1.disconnected(socketMetric_listen1_1, anyRemoteAddr); metrics_listen1_server2.disconnected(socketMetric_listen1_2, anyRemoteAddr); metrics_listen2_server1.disconnected(socketMetric_listen2_1, anyRemoteAddr); metrics_listen2_server2.disconnected(socketMetric_listen2_2, anyRemoteAddr); metrics_listen2_server2.disconnected(socketMetric_listen2_3, anyRemoteAddr); Assertions.assertFalse(socketMetric_listen1_1.isConnected()); Assertions.assertFalse(socketMetric_listen1_2.isConnected()); Assertions.assertFalse(socketMetric_listen2_1.isConnected()); Assertions.assertFalse(socketMetric_listen2_2.isConnected()); Assertions.assertFalse(socketMetric_listen2_3.isConnected()); Assertions.assertEquals(0, endpointMetric1.getCurrentConnectionCount()); Assertions.assertEquals(0, endpointMetric2.getCurrentConnectionCount()); } @Test public void bytesRead() { metrics_listen1_server1.bytesRead(socketMetric_listen1_1, anyRemoteAddr, 1); metrics_listen1_server2.bytesRead(socketMetric_listen1_2, anyRemoteAddr, 2); metrics_listen2_server1.bytesRead(socketMetric_listen2_1, anyRemoteAddr, 3); metrics_listen2_server2.bytesRead(socketMetric_listen2_2, anyRemoteAddr, 4); metrics_listen2_server2.bytesRead(socketMetric_listen2_3, anyRemoteAddr, 5); Assertions.assertEquals(3, endpointMetric1.getBytesRead()); Assertions.assertEquals(12, endpointMetric2.getBytesRead()); } @Test public void bytesWritten() { metrics_listen1_server1.bytesWritten(socketMetric_listen1_1, anyRemoteAddr, 1); metrics_listen1_server2.bytesWritten(socketMetric_listen1_2, anyRemoteAddr, 2); metrics_listen2_server1.bytesWritten(socketMetric_listen2_1, anyRemoteAddr, 3); metrics_listen2_server2.bytesWritten(socketMetric_listen2_2, anyRemoteAddr, 4); metrics_listen2_server2.bytesWritten(socketMetric_listen2_3, anyRemoteAddr, 5); Assertions.assertEquals(3, endpointMetric1.getBytesWritten()); Assertions.assertEquals(12, endpointMetric2.getBytesWritten()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import org.junit.Test; import io.vertx.core.VertxOptions; import io.vertx.core.metrics.MetricsOptions; import io.vertx.core.spi.metrics.VertxMetrics; import org.junit.jupiter.api.Assertions; public class TestDefaultVertxMetricsFactory { VertxOptions options = new VertxOptions(); DefaultVertxMetricsFactory factory = new DefaultVertxMetricsFactory(); @Test public void metrics() { MetricsOptions metricsOptions = factory.newOptions(); options.setMetricsOptions(metricsOptions); VertxMetrics vertxMetrics = factory.metrics(options); Assertions.assertTrue(metricsOptions.isEnabled()); Assertions.assertSame(factory.getVertxMetrics(), vertxMetrics); Assertions.assertTrue(vertxMetrics.isMetricsEnabled()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestMetricsOptionsEx.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.metrics; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMetricsOptionsEx { MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); @Test public void interval() { Assertions.assertEquals(TimeUnit.MINUTES.toMillis(1), metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds()); metricsOptionsEx.setCheckClientEndpointMetricIntervalInMinute(2); Assertions.assertEquals(TimeUnit.MINUTES.toMillis(2), metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds()); } @Test public void expired() { Assertions.assertEquals(TimeUnit.MINUTES.toNanos(15), metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano()); metricsOptionsEx.setCheckClientEndpointMetricExpiredInNano(10); Assertions.assertEquals(10, metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano()); metricsOptionsEx.setCheckClientEndpointMetricExpiredInMinute(60); Assertions.assertEquals(TimeUnit.MINUTES.toNanos(60), metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.server; import java.io.UnsupportedEncodingException; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import io.vertx.core.buffer.Buffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTcpParser { long msgId; Buffer headerBuffer; Buffer bodyBuffer; @Test public void test() throws UnsupportedEncodingException { TcpBufferHandler output = (_msgId, _headerBuffer, _bodyBuffer) -> { msgId = _msgId; headerBuffer = _headerBuffer; bodyBuffer = _bodyBuffer; }; byte[] header = new byte[] {1, 2, 3}; byte[] body = new byte[] {1, 2, 3, 4}; TcpOutputStream os = new TcpOutputStream(1); os.writeInt(header.length + body.length); os.writeInt(header.length); os.write(header); os.write(body); TcpParser parser = new TcpParser(output); parser.handle(os.getBuffer()); os.close(); Assertions.assertEquals(1, msgId); Assertions.assertArrayEquals(header, headerBuffer.getBytes()); Assertions.assertArrayEquals(body, bodyBuffer.getBytes()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServerConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.server; import io.vertx.core.net.SocketAddress; import io.vertx.core.net.impl.NetSocketImpl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestTcpServerConnection { @Test public void test() { SocketAddress socketAddress = Mockito.mock(SocketAddress.class); NetSocketImpl netSocket = Mockito.mock(NetSocketImpl.class); Mockito.when(netSocket.remoteAddress()).thenReturn(socketAddress); TcpServerConnection connection = new TcpServerConnection(); connection.setProtocol("p"); connection.setZipName("z"); connection.init(netSocket); Assertions.assertEquals(netSocket, connection.getNetSocket()); } } ================================================ FILE: foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.foundation.vertx.stream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import io.vertx.core.buffer.Buffer; public class TestBufferInputStream { private BufferInputStream instance; @Before public void setUp() throws Exception { Buffer buffer = Mockito.mock(Buffer.class); instance = new BufferInputStream(buffer); } @After public void tearDown() throws Exception { instance = null; } @Test public void testRead() { Assertions.assertEquals(0, instance.read()); } @Test public void testReadDecorate() throws IOException { String text = "abcdefg123456789"; ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(out); gzipOutputStream.write(text.getBytes()); gzipOutputStream.close(); Buffer buffer = Buffer.buffer(); buffer.appendBytes(out.toByteArray()); out.close(); BufferInputStream bufferInputStream = new BufferInputStream(buffer); GZIPInputStream gzipInputStream = new GZIPInputStream(bufferInputStream); StringBuilder sb = new StringBuilder(); byte[] bufferByte = new byte[256]; int n; while ((n = gzipInputStream.read(bufferByte)) >= 0) { sb.append(new String(bufferByte, 0, n)); } gzipInputStream.close(); Assertions.assertEquals(text, sb.toString()); } @Test public void testReadByteArray() { byte[] b = "csr".getBytes(StandardCharsets.UTF_8); Assertions.assertEquals(-1, instance.read(b)); } @Test public void testReadByteArrayIntInt() { byte[] b = "csr".getBytes(StandardCharsets.UTF_8); Assertions.assertEquals(-1, instance.read(b, 1, 0)); } @Test public void testSkip() { Assertions.assertEquals(0, instance.skip(1)); } @Test public void testAvailable() { Assertions.assertEquals(0, instance.available()); } @Test public void testClose() { try { instance.close(); } catch (Exception e) { Assertions.fail(); // This assertion is made to fail the test case in case the close() throws exception } } @Test public void testBufferInputStream() { Assertions.assertNotNull(instance); } @Test public void testReadBoolean() { Assertions.assertFalse(instance.readBoolean()); } @Test public void testReadShort() { Assertions.assertEquals(0, instance.readShort()); } @Test public void testReadInt() { Assertions.assertEquals(0, instance.readInt()); } @Test public void testReadLong() { Assertions.assertEquals(0, instance.readLong()); } @Test public void testGetIndex() { Assertions.assertEquals(0, instance.getIndex()); } @Test public void testReadString() { Assertions.assertNotNull(instance.readString()); } } ================================================ FILE: foundations/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default foundations Java Chassis::Foundations pom foundation-spi foundation-vertx foundation-common foundation-config foundation-metrics foundation-ssl foundation-test-scaffolding foundation-protobuf foundation-registry ================================================ FILE: governance/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 servicecomb-governance Java Chassis::Governance org.apache.servicecomb foundation-common io.github.resilience4j resilience4j-all io.github.resilience4j resilience4j-micrometer io.micrometer micrometer-core org.apache.commons commons-lang3 org.springframework spring-beans org.springframework spring-context org.yaml snakeyaml com.google.guava guava com.google.guava failureaccess test org.springframework spring-test test org.springframework.boot spring-boot-test test io.micrometer micrometer-registry-prometheus test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.Map; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; import org.apache.servicecomb.governance.handler.FaultInjectionHandler; import org.apache.servicecomb.governance.handler.GovernanceCacheHandler; import org.apache.servicecomb.governance.handler.IdentifierRateLimitingHandler; import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; import org.apache.servicecomb.governance.handler.LoadBalanceHandler; import org.apache.servicecomb.governance.handler.MapperHandler; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.apache.servicecomb.governance.handler.RetryHandler; import org.apache.servicecomb.governance.handler.TimeLimiterHandler; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.governance.marker.operator.CompareOperator; import org.apache.servicecomb.governance.marker.operator.ContainsOperator; import org.apache.servicecomb.governance.marker.operator.ExactOperator; import org.apache.servicecomb.governance.marker.operator.MatchOperator; import org.apache.servicecomb.governance.marker.operator.PrefixOperator; import org.apache.servicecomb.governance.marker.operator.SuffixOperator; import org.apache.servicecomb.governance.properties.BulkheadProperties; import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; import org.apache.servicecomb.governance.properties.FaultInjectionProperties; import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.properties.IdentifierRateLimitProperties; import org.apache.servicecomb.governance.properties.InstanceBulkheadProperties; import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; import org.apache.servicecomb.governance.properties.LoadBalanceProperties; import org.apache.servicecomb.governance.properties.MapperProperties; import org.apache.servicecomb.governance.properties.MatchProperties; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.apache.servicecomb.governance.properties.RetryProperties; import org.apache.servicecomb.governance.properties.TimeLimiterProperties; import org.apache.servicecomb.governance.service.MatchersService; import org.apache.servicecomb.governance.service.MatchersServiceImpl; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import io.micrometer.core.instrument.MeterRegistry; @Configuration public class GovernanceCommonConfiguration { // properties configuration @Bean public BulkheadProperties scbBulkheadProperties() { return new BulkheadProperties(); } @Bean public InstanceBulkheadProperties scbInstanceBulkheadProperties() { return new InstanceBulkheadProperties(); } @Bean public CircuitBreakerProperties scbCircuitBreakerProperties() { return new CircuitBreakerProperties(); } @Bean public InstanceIsolationProperties scbInstanceIsolationProperties() { return new InstanceIsolationProperties(); } @Bean public MatchProperties scbMatchProperties() { return new MatchProperties(); } @Bean public RateLimitProperties scbRateLimitProperties() { return new RateLimitProperties(); } @Bean public IdentifierRateLimitProperties scbIdentifierRateLimitProperties() { return new IdentifierRateLimitProperties(); } @Bean public RetryProperties scbRetryProperties() { return new RetryProperties(); } @Bean public TimeLimiterProperties scbTimeLimiterProperties() { return new TimeLimiterProperties(); } @Bean public GovernanceCacheProperties scbCacheProperties() { return new GovernanceCacheProperties(); } @Bean public FaultInjectionProperties scbFaultInjectionProperties() { return new FaultInjectionProperties(); } @Bean public LoadBalanceProperties scbLoadBalanceProperties() { return new LoadBalanceProperties(); } @Bean public MapperProperties scbMapperProperties() { return new MapperProperties(); } // handlers configuration @Bean public BulkheadHandler scbBulkheadHandler(BulkheadProperties scbBulkheadProperties) { return new BulkheadHandler(scbBulkheadProperties); } @Bean public InstanceBulkheadHandler scbInstanceBulkheadHandler(InstanceBulkheadProperties scbInstanceBulkheadProperties) { return new InstanceBulkheadHandler(scbInstanceBulkheadProperties); } @Bean public LoadBalanceHandler scbLoadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { return new LoadBalanceHandler(loadBalanceProperties); } @Bean public CircuitBreakerHandler scbCircuitBreakerHandler(CircuitBreakerProperties scbCircuitBreakerProperties, AbstractCircuitBreakerExtension circuitBreakerExtension) { return new CircuitBreakerHandler(scbCircuitBreakerProperties, circuitBreakerExtension); } @Bean public InstanceIsolationHandler scbInstanceIsolationHandler(InstanceIsolationProperties scbInstanceIsolationProperties, AbstractInstanceIsolationExtension isolationExtension, ObjectProvider meterRegistry) { return new InstanceIsolationHandler(scbInstanceIsolationProperties, isolationExtension, meterRegistry); } @Bean public RateLimitingHandler scbRateLimitingHandler(RateLimitProperties rateLimitProperties) { return new RateLimitingHandler(rateLimitProperties); } @Bean public IdentifierRateLimitingHandler scbIdentifierRateLimitingHandler( IdentifierRateLimitProperties identifierRateLimitProperties) { return new IdentifierRateLimitingHandler(identifierRateLimitProperties); } @Bean public RetryHandler scbRetryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { return new RetryHandler(retryProperties, retryExtension); } @Bean public TimeLimiterHandler scbTimeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { return new TimeLimiterHandler(timeLimiterProperties); } @Bean public GovernanceCacheHandler scbGovernanceCacheHandler(GovernanceCacheProperties cacheProperties) { return new GovernanceCacheHandler(cacheProperties); } @Bean public FaultInjectionHandler scbFaultInjectionHandler(FaultInjectionProperties scbFaultInjectionProperties) { return new FaultInjectionHandler(scbFaultInjectionProperties); } @Bean public MapperHandler scbMapperHandler(MapperProperties scbMapperProperties) { return new MapperHandler(scbMapperProperties); } // request processor @Bean public RequestProcessor scbRequestProcessor(Map operatorMap) { return new RequestProcessor(operatorMap); } // matchers @Bean public MatchersService scbMatchersService(RequestProcessor requestProcessor, MatchProperties scbMatchProperties) { return new MatchersServiceImpl(requestProcessor, scbMatchProperties); } @Bean public MatchersManager scbMatchersManager(MatchersService matchersService) { return new MatchersManager(matchersService); } // operators @Bean public CompareOperator scbCompareOperator() { return new CompareOperator(); } @Bean public ContainsOperator scbContainsOperator() { return new ContainsOperator(); } @Bean public ExactOperator scbExactOperator() { return new ExactOperator(); } @Bean public PrefixOperator scbPrefixOperator() { return new PrefixOperator(); } @Bean public SuffixOperator scbSuffixOperator() { return new SuffixOperator(); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/MatchersManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.AbstractPolicy; import org.apache.servicecomb.governance.service.MatchersService; public class MatchersManager { private MatchersService matchersService; public MatchersManager(MatchersService matchersService) { this.matchersService = matchersService; } public T match(GovernanceRequestExtractor requestExtractor, Map policies) { List sortPolicies = new ArrayList<>(policies.size()); sortPolicies.addAll(policies.values()); sortPolicies.sort(T::compareTo); for (T policy : sortPolicies) { if (matchersService.checkMatch(requestExtractor, policy.getName())) { return policy; } } return null; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/MicroserviceMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; public interface MicroserviceMeta { String getName(); String getVersion(); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/entity/Configurable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.entity; /** * Indicates a object can be configure in configuration file or config center. */ public abstract class Configurable { protected String name; protected String services; public abstract boolean isValid(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getServices() { return this.services; } public void setServices(String services) { this.services = services; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceConfigurationChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.event; import java.util.Set; public class GovernanceConfigurationChangedEvent { private final Set changedConfigurations; public GovernanceConfigurationChangedEvent(Set changedConfigurations) { this.changedConfigurations = changedConfigurations; } public Set getChangedConfigurations() { return changedConfigurations; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceEventManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.event; import com.google.common.eventbus.EventBus; public class GovernanceEventManager { private static final EventBus eventBus = new EventBus(); public static EventBus getEventBus() { return eventBus; } public static void post(Object event) { eventBus.post(event); } public static void register(Object subscriber) { eventBus.register(subscriber); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/exception/IllegalArgsOperatorException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.exception; public class IllegalArgsOperatorException extends RuntimeException { private static final long serialVersionUID = 793575987576638624L; public IllegalArgsOperatorException(String message) { super(message); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import org.apache.servicecomb.governance.MatchersManager; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.AbstractPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.eventbus.Subscribe; import io.micrometer.core.instrument.MeterRegistry; public abstract class AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGovernanceHandler.class); protected final DisposableMap processors; private final Object lock = new Object(); protected MatchersManager matchersManager; protected MeterRegistry meterRegistry; protected AbstractGovernanceHandler() { GovernanceEventManager.register(this); processors = new DisposableMap<>(this::onConfigurationChanged); } @Autowired public void setMatchersManager(MatchersManager matchersManager) { this.matchersManager = matchersManager; } @Autowired(required = false) public void setMeterRegistry(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; } public PROCESSOR getActuator(GovernanceRequestExtractor requestExtractor) { POLICY policy = matchPolicy(requestExtractor); if (policy == null) { return null; } String key = createKey(requestExtractor, policy); if (key == null) { return null; } Disposable processor = processors.get(key); if (processor == null) { synchronized (lock) { processor = processors.get(key); if (processor == null) { processor = createProcessor(key, requestExtractor, policy); processors.put(key, processor); } } } return processor.get(); } protected abstract String createKey(GovernanceRequestExtractor requestExtractor, POLICY policy); protected abstract POLICY matchPolicy(GovernanceRequestExtractor requestExtractor); protected abstract Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, POLICY policy); protected void onConfigurationChanged(String key) { Disposable processor = processors.remove(key); if (processor != null) { LOGGER.info("remove governance processor {}", key); processor.dispose(); } } @Subscribe public void onDynamicConfigurationListener(GovernanceConfigurationChangedEvent event) { event.getChangedConfigurations().forEach(this::onConfigurationChanged); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.BulkheadPolicy; import org.apache.servicecomb.governance.properties.BulkheadProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadConfig; import io.github.resilience4j.bulkhead.BulkheadRegistry; import io.github.resilience4j.micrometer.tagged.BulkheadMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics; public class BulkheadHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(BulkheadHandler.class); private final BulkheadProperties bulkheadProperties; public BulkheadHandler(BulkheadProperties bulkheadProperties) { this.bulkheadProperties = bulkheadProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { return bulkheadProperties.getConfigKey() + "." + policy.getName(); } @Override public BulkheadPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, bulkheadProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { return getBulkhead(key, policy); } private Disposable getBulkhead(String key, BulkheadPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); BulkheadConfig config = BulkheadConfig.custom() .maxConcurrentCalls(policy.getMaxConcurrentCalls()) .maxWaitDuration(Duration.parse(policy.getMaxWaitDuration())) .build(); BulkheadRegistry registry = BulkheadRegistry.of(config); if (meterRegistry != null) { TaggedBulkheadMetrics .ofBulkheadRegistry(BulkheadMetricNames.custom() .availableConcurrentCallsMetricName(bulkheadProperties.getConfigKey() + ".available.concurrent.calls") .maxAllowedConcurrentCallsMetricName( bulkheadProperties.getConfigKey() + ".max.allowed.concurrent.calls").build(), registry) .bindTo(meterRegistry); } return new DisposableBulkhead(key, registry, registry.bulkhead(key)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.micrometer.tagged.CircuitBreakerMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; public class CircuitBreakerHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreakerHandler.class); private final CircuitBreakerProperties circuitBreakerProperties; private final AbstractCircuitBreakerExtension circuitBreakerExtension; public CircuitBreakerHandler(CircuitBreakerProperties circuitBreakerProperties, AbstractCircuitBreakerExtension circuitBreakerExtension) { this.circuitBreakerProperties = circuitBreakerProperties; this.circuitBreakerExtension = circuitBreakerExtension; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, CircuitBreakerPolicy policy) { return this.circuitBreakerProperties.getConfigKey() + "." + policy.getName(); } @Override public CircuitBreakerPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, circuitBreakerProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, CircuitBreakerPolicy policy) { return getCircuitBreaker(key, policy); } private Disposable getCircuitBreaker(String key, CircuitBreakerPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy); CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .failureRateThreshold(policy.getFailureRateThreshold()) .slowCallRateThreshold(policy.getSlowCallRateThreshold()) .waitDurationInOpenState(Duration.parse(policy.getWaitDurationInOpenState())) .slowCallDurationThreshold(Duration.parse(policy.getSlowCallDurationThreshold())) .permittedNumberOfCallsInHalfOpenState(policy.getPermittedNumberOfCallsInHalfOpenState()) .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) .slidingWindowType(policy.getSlidingWindowTypeEnum()) .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) .recordException(e -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), e)) .recordResult(r -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), r)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); if (meterRegistry != null) { TaggedCircuitBreakerMetrics .ofCircuitBreakerRegistry(CircuitBreakerMetricNames.custom() .callsMetricName(this.circuitBreakerProperties.getConfigKey() + ".calls") .notPermittedCallsMetricName(this.circuitBreakerProperties.getConfigKey() + ".not.permitted.calls") .stateMetricName(this.circuitBreakerProperties.getConfigKey() + ".state") .bufferedCallsMetricName(this.circuitBreakerProperties.getConfigKey() + ".buffered.calls") .slowCallsMetricName(this.circuitBreakerProperties.getConfigKey() + ".slow.calls") .failureRateMetricName(this.circuitBreakerProperties.getConfigKey() + ".failure.rate") .slowCallRateMetricName(this.circuitBreakerProperties.getConfigKey() + ".slow.call.rate") .build(), circuitBreakerRegistry) .bindTo(meterRegistry); } return new DisposableCircuitBreaker(key, circuitBreakerRegistry, circuitBreakerRegistry.circuitBreaker(key, circuitBreakerConfig)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/Disposable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; public abstract class Disposable { private long lastAccessed; protected Disposable() { lastAccessed = System.currentTimeMillis(); } public abstract void dispose(); public V get() { lastAccessed = System.currentTimeMillis(); return getValue(); } public abstract V getValue(); public abstract String getKey(); public long lastAccessed() { return lastAccessed; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableBulkhead.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadRegistry; public class DisposableBulkhead extends Disposable { private final String key; private final BulkheadRegistry bulkheadRegistry; private final Bulkhead bulkhead; public DisposableBulkhead(String key, BulkheadRegistry bulkheadRegistry, Bulkhead bulkhead) { this.key = key; this.bulkheadRegistry = bulkheadRegistry; this.bulkhead = bulkhead; } @Override public void dispose() { bulkheadRegistry.remove(key); } @Override public Bulkhead getValue() { return bulkhead; } @Override public String getKey() { return key; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableCircuitBreaker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; public class DisposableCircuitBreaker extends Disposable { private final CircuitBreaker circuitBreaker; private final CircuitBreakerRegistry circuitBreakerRegistry; private final String key; public DisposableCircuitBreaker(String key, CircuitBreakerRegistry registry, CircuitBreaker circuitBreaker) { this.key = key; this.circuitBreaker = circuitBreaker; this.circuitBreakerRegistry = registry; } @Override public void dispose() { this.circuitBreakerRegistry.remove(key); } @Override public CircuitBreaker getValue() { return circuitBreaker; } @Override public String getKey() { return key; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableHolder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; public class DisposableHolder extends Disposable { private final E e; private final String key; public DisposableHolder(String key, E e) { this.key = key; this.e = e; } @Override public void dispose() { } @Override public E getValue() { return e; } @Override public String getKey() { return key; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableMap.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * check if some items are expired when put new item to map and remove expired items. */ public class DisposableMap extends ConcurrentHashMap> { private static final long serialVersionUID = 7249069246763182397L; public interface RemoveListener { void onRemoveEntry(String k); } private static final int EXPIRE_TIME = 10 * 60 * 1000; private final RemoveListener listener; public DisposableMap(RemoveListener listener) { this.listener = listener; } @Override public Disposable put(String key, Disposable value) { Disposable result = super.put(key, value); checkExpired(); return result; } private void checkExpired() { List expired = new ArrayList<>(); this.values().forEach(v -> { if (System.currentTimeMillis() - v.lastAccessed() >= EXPIRE_TIME) { expired.add(v.getKey()); } }); expired.forEach(listener::onRemoveEntry); } @Override public Disposable get(Object key) { return super.get(key); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRateLimiter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; public class DisposableRateLimiter extends Disposable { private final String key; private final RateLimiter rateLimiter; private final RateLimiterRegistry registry; public DisposableRateLimiter(String key, RateLimiter rateLimiter, RateLimiterRegistry registry) { this.key = key; this.rateLimiter = rateLimiter; this.registry = registry; } @Override public void dispose() { registry.remove(key); } @Override public RateLimiter getValue() { return rateLimiter; } @Override public String getKey() { return key; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRetry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryRegistry; public class DisposableRetry extends Disposable { private final String key; private final RetryRegistry registry; private final Retry retry; public DisposableRetry(String key, RetryRegistry registry, Retry retry) { this.key = key; this.registry = registry; this.retry = retry; } @Override public void dispose() { registry.remove(key); } @Override public Retry getValue() { return retry; } @Override public String getKey() { return key; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableTimeLimiter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import io.github.resilience4j.timelimiter.TimeLimiter; import io.github.resilience4j.timelimiter.TimeLimiterRegistry; public class DisposableTimeLimiter extends Disposable { private final String key; private final TimeLimiterRegistry timeLimiterRegistry; private final TimeLimiter timeLimiter; public DisposableTimeLimiter(String key, TimeLimiterRegistry registry, TimeLimiter timeLimiter) { this.key = key; this.timeLimiterRegistry = registry; this.timeLimiter = timeLimiter; } @Override public void dispose() { timeLimiterRegistry.remove(key); } @Override public TimeLimiter getValue() { return timeLimiter; } @Override public String getKey() { return key; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; import org.apache.servicecomb.governance.processor.injection.Fault; import org.apache.servicecomb.governance.processor.injection.FaultInjectionUtil; import org.apache.servicecomb.governance.properties.FaultInjectionProperties; public class FaultInjectionHandler extends AbstractGovernanceHandler { private final FaultInjectionProperties faultInjectionProperties; public FaultInjectionHandler(FaultInjectionProperties faultInjectionProperties) { this.faultInjectionProperties = faultInjectionProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, FaultInjectionPolicy policy) { return FaultInjectionProperties.MATCH_FAULT_INJECTION_KEY + "." + policy.getName(); } @Override public FaultInjectionPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, faultInjectionProperties.getParsedEntity()); } @Override protected DisposableHolder createProcessor(String key, GovernanceRequestExtractor requestExtractor, FaultInjectionPolicy policy) { return new DisposableHolder<>(key, FaultInjectionUtil.getFault(key, policy)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/GovernanceCacheHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.service.GovernanceCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; public class GovernanceCacheHandler extends AbstractGovernanceHandler, GovernanceCachePolicy> { private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceCacheHandler.class); private final GovernanceCacheProperties cacheProperties; public GovernanceCacheHandler(GovernanceCacheProperties cacheProperties) { this.cacheProperties = cacheProperties; } @Override public GovernanceCachePolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, cacheProperties.getParsedEntity()); } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, GovernanceCachePolicy policy) { return cacheProperties.getConfigKey() + "." + policy.getName(); } @Override protected Disposable> createProcessor(String key, GovernanceRequestExtractor requestExtractor, GovernanceCachePolicy policy) { return getGovernanceCache(key, policy); } protected Disposable> getGovernanceCache(String key, GovernanceCachePolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); Cache cache = CacheBuilder.newBuilder() .expireAfterWrite(Duration.parse(policy.getTtl())) .maximumSize(policy.getMaximumSize()) .concurrencyLevel(policy.getConcurrencyLevel()) .build(); GovernanceCache governanceCache = GovernanceCache.of(cache); return new DisposableHolder<>(key, governanceCache); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/IdentifierRateLimitingHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.IdentifierRateLimitingPolicy; import org.apache.servicecomb.governance.properties.IdentifierRateLimitProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.micrometer.tagged.RateLimiterMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; public class IdentifierRateLimitingHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(IdentifierRateLimitingHandler.class); private final IdentifierRateLimitProperties rateLimitProperties; public IdentifierRateLimitingHandler(IdentifierRateLimitProperties rateLimitProperties) { this.rateLimitProperties = rateLimitProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, IdentifierRateLimitingPolicy policy) { if (StringUtils.isEmpty(policy.getIdentifier()) || StringUtils.isEmpty(requestExtractor.header(policy.getIdentifier()))) { LOGGER.info("identifier rate limiting is not properly configured, identifier is empty."); return null; } return this.rateLimitProperties.getConfigKey() + "." + policy.getName() + "." + requestExtractor.header(policy.getIdentifier()); } @Override protected void onConfigurationChanged(String key) { if (key.startsWith(this.rateLimitProperties.getConfigKey())) { for (String processorKey : processors.keySet()) { if (processorKey.startsWith(key)) { Disposable processor = processors.remove(processorKey); if (processor != null) { LOGGER.info("remove identifier rate limiting processor {}", key); processor.dispose(); } } } } } @Override public IdentifierRateLimitingPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, rateLimitProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, IdentifierRateLimitingPolicy policy) { return getRateLimiter(key, policy); } private Disposable getRateLimiter(String key, IdentifierRateLimitingPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); RateLimiterConfig config = RateLimiterConfig.custom() .limitForPeriod(policy.getRate()) .limitRefreshPeriod(Duration.parse(policy.getLimitRefreshPeriod())) .timeoutDuration(Duration.parse(policy.getTimeoutDuration())) .build(); RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config); if (meterRegistry != null) { TaggedRateLimiterMetrics .ofRateLimiterRegistry(RateLimiterMetricNames.custom() .availablePermissionsMetricName( this.rateLimitProperties.getConfigKey() + ".available.permissions") .waitingThreadsMetricName(this.rateLimitProperties.getConfigKey() + ".waiting.threads") .build(), rateLimiterRegistry) .bindTo(meterRegistry); } return new DisposableRateLimiter(key, rateLimiterRegistry.rateLimiter(key), rateLimiterRegistry); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceBulkheadHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.BulkheadPolicy; import org.apache.servicecomb.governance.properties.InstanceBulkheadProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadConfig; import io.github.resilience4j.bulkhead.BulkheadRegistry; import io.github.resilience4j.micrometer.tagged.BulkheadMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics; public class InstanceBulkheadHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(InstanceBulkheadHandler.class); private final InstanceBulkheadProperties bulkheadProperties; public InstanceBulkheadHandler(InstanceBulkheadProperties bulkheadProperties) { this.bulkheadProperties = bulkheadProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { return this.bulkheadProperties.getConfigKey() + "." + policy.getName() + "." + requestExtractor.serviceName() + "." + requestExtractor.instanceId(); } @Override protected void onConfigurationChanged(String key) { if (key.startsWith(this.bulkheadProperties.getConfigKey())) { for (String processorKey : processors.keySet()) { if (processorKey.startsWith(key)) { Disposable processor = processors.remove(processorKey); if (processor != null) { LOGGER.info("remove instance bulkhead processor {}", key); processor.dispose(); } } } } } @Override public BulkheadPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { if (StringUtils.isEmpty(requestExtractor.serviceName()) || StringUtils.isEmpty( requestExtractor.instanceId())) { LOGGER.info("Instance bulkhead is not properly configured, service id or instance id is empty."); return null; } return matchersManager.match(requestExtractor, bulkheadProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { return getBulkhead(key, policy); } private Disposable getBulkhead(String key, BulkheadPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); BulkheadConfig config = BulkheadConfig.custom() .maxConcurrentCalls(policy.getMaxConcurrentCalls()) .maxWaitDuration(Duration.parse(policy.getMaxWaitDuration())) .build(); BulkheadRegistry registry = BulkheadRegistry.of(config); if (meterRegistry != null) { TaggedBulkheadMetrics .ofBulkheadRegistry(BulkheadMetricNames.custom() .availableConcurrentCallsMetricName( this.bulkheadProperties.getConfigKey() + ".available.concurrent.calls") .maxAllowedConcurrentCallsMetricName( this.bulkheadProperties.getConfigKey() + ".max.allowed.concurrent.calls").build(), registry) .bindTo(meterRegistry); } return new DisposableBulkhead(key, registry, registry.bulkhead(key, config)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.micrometer.tagged.CircuitBreakerMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; import io.micrometer.core.instrument.MeterRegistry; public final class InstanceIsolationHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(InstanceIsolationHandler.class); private final InstanceIsolationProperties instanceIsolationProperties; private final AbstractInstanceIsolationExtension isolationExtension; private final MeterRegistry meterRegistry; public InstanceIsolationHandler(InstanceIsolationProperties instanceIsolationProperties, AbstractInstanceIsolationExtension isolationExtension, ObjectProvider meterRegistry) { this.instanceIsolationProperties = instanceIsolationProperties; this.isolationExtension = isolationExtension; this.meterRegistry = meterRegistry.getIfAvailable(); } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, CircuitBreakerPolicy policy) { return this.instanceIsolationProperties.getConfigKey() + "." + policy.getName() + "." + requestExtractor.serviceName() + "." + requestExtractor.instanceId(); } @Override protected void onConfigurationChanged(String key) { if (key.startsWith(this.instanceIsolationProperties.getConfigKey())) { for (String processorKey : processors.keySet()) { if (processorKey.startsWith(key)) { Disposable circuitBreaker = processors.remove(processorKey); if (circuitBreaker != null) { LOGGER.info("remove instance isolation processor {}", key); circuitBreaker.dispose(); } } } } } @Override public CircuitBreakerPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { if (StringUtils.isEmpty(requestExtractor.serviceName()) || StringUtils.isEmpty( requestExtractor.instanceId())) { LOGGER.debug("Isolation is not properly configured, service id or instance id is empty."); return null; } return matchersManager.match(requestExtractor, instanceIsolationProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, CircuitBreakerPolicy policy) { return getCircuitBreaker(key, policy); } private Disposable getCircuitBreaker(String key, CircuitBreakerPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .failureRateThreshold(policy.getFailureRateThreshold()) .slowCallRateThreshold(policy.getSlowCallRateThreshold()) .waitDurationInOpenState(Duration.parse(policy.getWaitDurationInOpenState())) .slowCallDurationThreshold(Duration.parse(policy.getSlowCallDurationThreshold())) .permittedNumberOfCallsInHalfOpenState(policy.getPermittedNumberOfCallsInHalfOpenState()) .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) .slidingWindowType(policy.getSlidingWindowTypeEnum()) .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) .recordException(e -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), e)) .recordResult(r -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), r)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); if (meterRegistry != null) { TaggedCircuitBreakerMetrics .ofCircuitBreakerRegistry(CircuitBreakerMetricNames.custom() .callsMetricName(this.instanceIsolationProperties.getConfigKey() + ".calls") .notPermittedCallsMetricName( this.instanceIsolationProperties.getConfigKey() + ".not.permitted.calls") .stateMetricName(this.instanceIsolationProperties.getConfigKey() + ".state") .bufferedCallsMetricName(this.instanceIsolationProperties.getConfigKey() + ".buffered.calls") .slowCallsMetricName(this.instanceIsolationProperties.getConfigKey() + ".slow.calls") .failureRateMetricName(this.instanceIsolationProperties.getConfigKey() + ".failure.rate") .slowCallRateMetricName(this.instanceIsolationProperties.getConfigKey() + ".slow.call.rate") .build(), circuitBreakerRegistry) .bindTo(meterRegistry); } return new DisposableCircuitBreaker(key, circuitBreakerRegistry, circuitBreakerRegistry.circuitBreaker(key, circuitBreakerConfig)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/LoadBalanceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.LoadBalancerPolicy; import org.apache.servicecomb.governance.processor.loadbanlance.LoadBalance; import org.apache.servicecomb.governance.properties.LoadBalanceProperties; public class LoadBalanceHandler extends AbstractGovernanceHandler { private final LoadBalanceProperties loadBalanceProperties; public LoadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { this.loadBalanceProperties = loadBalanceProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, LoadBalancerPolicy policy) { return this.loadBalanceProperties.getConfigKey() + "." + policy.getName(); } @Override public LoadBalancerPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, loadBalanceProperties.getParsedEntity()); } @Override protected DisposableHolder createProcessor(String key, GovernanceRequestExtractor requestExtractor, LoadBalancerPolicy policy) { return new DisposableHolder<>(key, LoadBalance.getLoadBalance(key, policy)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/MapperHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.MapperPolicy; import org.apache.servicecomb.governance.processor.mapping.Mapper; import org.apache.servicecomb.governance.properties.MapperProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MapperHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingHandler.class); private final MapperProperties mapperProperties; public MapperHandler(MapperProperties mapperProperties) { this.mapperProperties = mapperProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, MapperPolicy policy) { return mapperProperties.getConfigKey() + "." + policy.getName(); } @Override public MapperPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, mapperProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, MapperPolicy policy) { return getMapper(key, policy); } private Disposable getMapper(String key, MapperPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); return new DisposableHolder<>(key, Mapper.create(policy.getTarget())); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.RateLimitingPolicy; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.micrometer.tagged.RateLimiterMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; public class RateLimitingHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingHandler.class); private final RateLimitProperties rateLimitProperties; public RateLimitingHandler(RateLimitProperties rateLimitProperties) { this.rateLimitProperties = rateLimitProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, RateLimitingPolicy policy) { return this.rateLimitProperties.getConfigKey() + "." + policy.getName(); } @Override public RateLimitingPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, rateLimitProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, RateLimitingPolicy policy) { return getRateLimiter(key, policy); } private Disposable getRateLimiter(String key, RateLimitingPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy.toString()); RateLimiterConfig config = RateLimiterConfig.custom() .limitForPeriod(policy.getRate()) .limitRefreshPeriod(Duration.parse(policy.getLimitRefreshPeriod())) .timeoutDuration(Duration.parse(policy.getTimeoutDuration())) .build(); RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config); if (meterRegistry != null) { TaggedRateLimiterMetrics .ofRateLimiterRegistry(RateLimiterMetricNames.custom() .availablePermissionsMetricName( this.rateLimitProperties.getConfigKey() + ".available.permissions") .waitingThreadsMetricName(this.rateLimitProperties.getConfigKey() + ".waiting.threads") .build(), rateLimiterRegistry) .bindTo(meterRegistry); } return new DisposableRateLimiter(key, rateLimiterRegistry.rateLimiter(key), rateLimiterRegistry); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.RetryPolicy; import org.apache.servicecomb.governance.properties.RetryProperties; import org.apache.servicecomb.governance.utils.GovernanceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.core.IntervalFunction; import io.github.resilience4j.micrometer.tagged.RetryMetricNames; import io.github.resilience4j.micrometer.tagged.TaggedRetryMetrics; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; import io.github.resilience4j.retry.RetryRegistry; public class RetryHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(RetryHandler.class); private final RetryProperties retryProperties; private final AbstractRetryExtension retryExtension; public RetryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { this.retryProperties = retryProperties; this.retryExtension = retryExtension; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, RetryPolicy policy) { return this.retryProperties.getConfigKey() + "." + policy.getName(); } @Override public RetryPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, retryProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, RetryPolicy policy) { return getRetry(key, policy); } private Disposable getRetry(String key, RetryPolicy retryPolicy) { LOGGER.info("applying new policy {} for {}", key, retryPolicy.toString()); RetryConfig config = RetryConfig.custom() .maxAttempts(retryPolicy.getMaxAttempts() + 1) .retryOnResult(response -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), response)) .retryOnException(exception -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), exception)) .intervalFunction(getIntervalFunction(retryPolicy)) .failAfterMaxAttempts(retryPolicy.isFailAfterMaxAttempts()) .build(); RetryRegistry registry = RetryRegistry.of(config); if (meterRegistry != null) { TaggedRetryMetrics .ofRetryRegistry(RetryMetricNames.custom() .callsMetricName(this.retryProperties.getConfigKey() + ".calls") .build(), registry) .bindTo(meterRegistry); } return new DisposableRetry(key, registry, registry.retry(key)); } private IntervalFunction getIntervalFunction(RetryPolicy retryPolicy) { if (GovernanceUtils.STRATEGY_RANDOM_BACKOFF.equals(retryPolicy.getRetryStrategy())) { return IntervalFunction.ofExponentialRandomBackoff(Duration.parse(retryPolicy.getInitialInterval()), retryPolicy.getMultiplier(), retryPolicy.getRandomizationFactor()); } return IntervalFunction.of(Duration.parse(retryPolicy.getWaitDuration())); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/TimeLimiterHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler; import java.time.Duration; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; import org.apache.servicecomb.governance.properties.TimeLimiterProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.micrometer.tagged.TaggedTimeLimiterMetrics; import io.github.resilience4j.micrometer.tagged.TimeLimiterMetricNames; import io.github.resilience4j.timelimiter.TimeLimiter; import io.github.resilience4j.timelimiter.TimeLimiterConfig; import io.github.resilience4j.timelimiter.TimeLimiterRegistry; public class TimeLimiterHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(TimeLimiterHandler.class); private final TimeLimiterProperties timeLimiterProperties; public TimeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { this.timeLimiterProperties = timeLimiterProperties; } @Override protected String createKey(GovernanceRequestExtractor requestExtractor, TimeLimiterPolicy policy) { return timeLimiterProperties.getConfigKey() + "." + policy.getName(); } @Override public TimeLimiterPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { return matchersManager.match(requestExtractor, timeLimiterProperties.getParsedEntity()); } @Override public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, TimeLimiterPolicy policy) { return getTimeLimiter(key, policy); } private Disposable getTimeLimiter(String key, TimeLimiterPolicy policy) { LOGGER.info("applying new policy {} for {}", key, policy); TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() .timeoutDuration(Duration.parse(policy.getTimeoutDuration())) .cancelRunningFuture(policy.isCancelRunningFuture()) .build(); TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.of(timeLimiterConfig); if (meterRegistry != null) { TaggedTimeLimiterMetrics.ofTimeLimiterRegistry(TimeLimiterMetricNames.custom() .callsMetricName(timeLimiterProperties.getConfigKey() + ".calls") .build(), timeLimiterRegistry).bindTo(meterRegistry); } return new DisposableTimeLimiter(key, timeLimiterRegistry, timeLimiterRegistry.timeLimiter(key)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractCircuitBreakerExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; public abstract class AbstractCircuitBreakerExtension extends AbstractFailurePredictor { } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractFailurePredictor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; import java.util.List; import java.util.stream.IntStream; import org.apache.commons.lang3.StringUtils; public abstract class AbstractFailurePredictor implements FailurePredictor { @Override public boolean isFailedResult(List statusList, Object result) { String statusCode = extractStatusCode(result); if (StringUtils.isEmpty(statusCode)) { return false; } return statusCodeContains(statusList, statusCode); } protected abstract String extractStatusCode(Object result); protected static boolean statusCodeContains(List statusList, String responseStatus) { return statusList.stream().anyMatch(status -> statusCodeMatch(status, responseStatus)); } private static boolean statusCodeMatch(String status, String responseStatus) { if (status == null) { return false; } if (responseStatus.length() != status.length()) { return false; } char[] statusChar = status.toCharArray(); char[] responseChar = responseStatus.toCharArray(); return IntStream.range(0, statusChar.length).noneMatch(i -> statusChar[i] != responseChar[i] && statusChar[i] != 'x'); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractInstanceIsolationExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; public abstract class AbstractInstanceIsolationExtension extends AbstractFailurePredictor { } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractRetryExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; public abstract class AbstractRetryExtension extends AbstractFailurePredictor { } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ClientRecoverPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; public interface ClientRecoverPolicy { T apply(Throwable th); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; import java.io.IOException; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.net.SocketTimeoutException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.net.ssl.SSLHandshakeException; import com.google.common.collect.ImmutableMap; import io.netty.handler.ssl.SslHandshakeTimeoutException; import io.vertx.core.VertxException; public interface FailurePredictor { Map, List> STRICT_RETRIABLE = ImmutableMap., List>builder() .put(ConnectException.class, Collections.emptyList()) .put(SocketTimeoutException.class, Collections.emptyList()) /* * deal with some special exceptions caused by the server side close the connection */ .put(IOException.class, Collections.singletonList("Connection reset by peer")) .put(VertxException.class, Collections.singletonList("Connection was closed")) .put(NoRouteToHostException.class, Collections.emptyList()) .put(SSLHandshakeException.class, Collections.emptyList()) .put(SslHandshakeTimeoutException.class, Collections.emptyList()) .build(); boolean isFailedResult(List statusList, Object result); default boolean isFailedResult(List statusList, Throwable e) { return canRetryForException(STRICT_RETRIABLE, e); } static boolean canRetryForException(Map, List> retryList, Throwable throwableToSearchIn) { // retry on exception type on message match int infiniteLoopPreventionCounter = 10; while (throwableToSearchIn != null && infiniteLoopPreventionCounter > 0) { infiniteLoopPreventionCounter--; for (Entry, List> c : retryList.entrySet()) { Class key = c.getKey(); if (key.isAssignableFrom(throwableToSearchIn.getClass())) { if (c.getValue() == null || c.getValue().isEmpty()) { return true; } else { String msg = throwableToSearchIn.getMessage(); for (String val : c.getValue()) { if (val.equals(msg)) { return true; } } } } } throwableToSearchIn = throwableToSearchIn.getCause(); } return false; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ServerRecoverPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; public interface ServerRecoverPolicy { T apply(Throwable th); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/CustomMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker; public class CustomMatcher { private String customMatcherHandler; private String customMatcherParameters; public String getCustomMatcherHandler() { return customMatcherHandler; } public void setCustomMatcherHandler(String customMatcherHandler) { this.customMatcherHandler = customMatcherHandler; } public String getCustomMatcherParameters() { return customMatcherParameters; } public void setCustomMatcherParameters(String customMatcherParameters) { this.customMatcherParameters = customMatcherParameters; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker; import java.util.Collections; import java.util.Map; import org.springframework.util.LinkedCaseInsensitiveMap; public class GovernanceRequest implements GovernanceRequestExtractor { /** * headers with this request, maybe null. * For provider: headers indicates the request headers to me. * For consumer: headers indicates the request headers to the target. */ private Map headers = Collections.emptyMap(); /** * Queries with this request, maybe null. * For provider: Queries indicates the request params to me. * For consumer: Queries indicates the request params to the target. */ private Map queries = Collections.emptyMap(); /** * api path with this request, maybe null. For REST, e.g. /foo/bar; For RPC, e.g. MySchema.sayHello * For provider: uri indicates the request uri to me. * For consumer: uri indicates the request uri to the target. */ private String apiPath; /** * method with this request, maybe null. * For provider: method indicates the request method to me. * For consumer: method indicates the request method to the target. */ private String method; /** * instance id with this request, maybe null. * For provider: instanceId indicates who calls me. * For consumer: instanceId indicates the target instance. */ private String instanceId; /** * microservice id (microservice name or application name + microservice name) with this request, maybe null. * For provider: serviceName indicates who calls me. * For consumer: serviceName indicates the target service. */ private String serviceName; /** * sourceRequest the source request for creating this governanceRequest * For provider: uri indicates the request to me. * For consumer: uri indicates the request to the target. * the type of sourceRequest could be ClientRequest, ServerWebExchange, HttpRequest, HttpServletRequest and so on, * User will use this request to extract the information he need */ private Object sourceRequest; @Override public String header(String key) { return headers.get(key); } @Override public String query(String key) { return queries.get(key); } public Map getHeaders() { return headers; } public Map getQueries() { return queries; } public void setHeaders(Map headers) { Map temp = new LinkedCaseInsensitiveMap<>(); temp.putAll(headers); this.headers = temp; } public void setQueries(Map queries) { Map temp = new LinkedCaseInsensitiveMap<>(); temp.putAll(queries); this.queries = temp; } @Override public String apiPath() { return apiPath; } public void setApiPath(String apiPath) { this.apiPath = apiPath; } @Override public String method() { return method; } public void setMethod(String method) { this.method = method; } @Override public String instanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } @Override public String serviceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } @Override public Object sourceRequest() { return sourceRequest; } public void setSourceRequest(Object sourceRequest) { this.sourceRequest = sourceRequest; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker; public interface GovernanceRequestExtractor { String apiPath(); String method(); String header(String key); String query(String key); String instanceId(); String serviceName(); Object sourceRequest(); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker; import java.util.List; import java.util.Map; import org.apache.servicecomb.governance.marker.operator.RawOperator; public class Matcher { private String name; private Map headers; private Map queries; private RawOperator apiPath; private List method; private String serviceName; private CustomMatcher customMatcher; public String getName() { return name; } public void setName(String name) { this.name = name; } public Map getHeaders() { return headers; } public void setHeaders(Map headers) { this.headers = headers; } public Map getQueries() { return queries; } public void setQueries(Map queries) { this.queries = queries; } public RawOperator getApiPath() { return apiPath; } public void setApiPath(RawOperator apiPath) { this.apiPath = apiPath; } public List getMethod() { return method; } public void setMethod(List method) { this.method = method; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public CustomMatcher getCustomMatcher() { return customMatcher; } public void setCustomMatcher(CustomMatcher customMatcher) { this.customMatcher = customMatcher; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.marker.operator.MatchOperator; import org.apache.servicecomb.governance.marker.operator.RawOperator; import org.apache.servicecomb.governance.utils.CustomMatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * Request Processor checks if a request matches a configuration. */ public class RequestProcessor implements ApplicationContextAware { private static final Logger LOGGER = LoggerFactory.getLogger(RequestProcessor.class); public static final String errorMessageForNotImplements = " didn't implement interface org.apache.servicecomb.governance.utils.CustomMatch"; public static final String errorMessageForAbstractClass = " should be a instantiable class rather than abstract class or other else"; public static final String infoMessageForCreatingClass = "is not in spring container, create one and register it to spring container"; private final Map operatorMap; private ApplicationContext applicationContext; public RequestProcessor(Map operatorMap) { this.operatorMap = new HashMap<>(operatorMap.size()); operatorMap.forEach((k, v) -> this.operatorMap.put(v.name(), v)); } public boolean match(GovernanceRequestExtractor request, Matcher matcher) { if (!methodMatch(request, matcher)) { return false; } if (!apiPathMatch(request, matcher)) { return false; } if (!headersMatch(request, matcher)) { return false; } if (!queriesMatch(request, matcher)) { return false; } if (!serviceNameMatch(request, matcher)) { return false; } return customMatch(request, matcher); } private boolean serviceNameMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getServiceName() == null) { return true; } return matcher.getServiceName().equals(request.serviceName()); } private boolean headersMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getHeaders() == null) { return true; } for (Entry entry : matcher.getHeaders().entrySet()) { if (request.header(entry.getKey()) == null || !operatorMatch(request.header(entry.getKey()), entry.getValue())) { return false; } } return true; } private boolean queriesMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getQueries() == null) { return true; } for (Entry entry : matcher.getQueries().entrySet()) { if (request.query(entry.getKey()) == null || !operatorMatch(request.query(entry.getKey()), entry.getValue())) { return false; } } return true; } private boolean apiPathMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getApiPath() == null) { return true; } return operatorMatch(request.apiPath(), matcher.getApiPath()); } private boolean methodMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getMethod() == null) { return true; } return matcher.getMethod().contains(request.method()); } private boolean operatorMatch(String str, RawOperator rawOperator) { if (rawOperator.isEmpty()) { return false; } for (Entry entry : rawOperator.entrySet()) { MatchOperator operator = operatorMap.get(entry.getKey()); if (operator == null) { LOGGER.error("unsupported operator:" + entry.getKey() + ", please use one of :" + operatorMap.keySet()); return false; } if (!operator.match(str, entry.getValue())) { return false; } } return true; } private boolean customMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getCustomMatcher() == null) { return true; } String customMatcherHandlerName = matcher.getCustomMatcher().getCustomMatcherHandler(); String customMatcherParameters = matcher.getCustomMatcher().getCustomMatcherParameters(); if (StringUtils.isEmpty(customMatcherHandlerName) || StringUtils.isEmpty(customMatcherParameters)) { return true; } CustomMatch customMatcherHandler = generateHandler(customMatcherHandlerName); return customMatcherHandler.matchRequest(request, customMatcherParameters); } private CustomMatch getBeanByHandlerName(String customMatcherHandler) { Object extractObject = null; if (applicationContext.containsBean(customMatcherHandler)) { extractObject = applicationContext.getBean(customMatcherHandler); if (!(extractObject instanceof CustomMatch)) { LOGGER.error("{} {}", customMatcherHandler, errorMessageForNotImplements); throw new RuntimeException(customMatcherHandler + errorMessageForNotImplements); } return (CustomMatch) extractObject; } return null; } public CustomMatch generateHandler(String customMatcherHandler) { CustomMatch extractObject = getBeanByHandlerName(customMatcherHandler); if (extractObject != null) { return extractObject; } LOGGER.info("{} {}", customMatcherHandler, infoMessageForCreatingClass); Class extractionHandlerClass = null; try { extractionHandlerClass = Class.forName(customMatcherHandler); } catch (ClassNotFoundException e) { LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } if (!CustomMatch.class.isAssignableFrom(extractionHandlerClass)) { LOGGER.error("{} {}", customMatcherHandler, errorMessageForNotImplements); throw new RuntimeException(customMatcherHandler + errorMessageForNotImplements); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(extractionHandlerClass); BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext; registry.registerBeanDefinition(customMatcherHandler, builder.getBeanDefinition()); try { extractObject = (CustomMatch) applicationContext.getBean(customMatcherHandler); return extractObject; } catch (BeansException e) { LOGGER.error(e.getMessage(), e); throw new RuntimeException(customMatcherHandler + errorMessageForAbstractClass, e); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/TrafficMarker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker; import java.util.List; import org.apache.servicecomb.governance.entity.Configurable; public class TrafficMarker extends Configurable { private List matches; @Override public boolean isValid() { if (matches == null || matches.isEmpty()) { return false; } return true; } public List getMatches() { return matches; } public void setMatches(List matches) { this.matches = matches; } public boolean checkMatch(GovernanceRequestExtractor extractor, RequestProcessor requestProcessor) { return this.matches.stream().anyMatch(match -> requestProcessor.match(extractor, match)); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; import java.util.HashSet; import java.util.Set; import org.apache.servicecomb.governance.exception.IllegalArgsOperatorException; public class CompareOperator implements MatchOperator { private final Set charSet = new HashSet<>(); public CompareOperator() { charSet.add('>'); charSet.add('<'); charSet.add('='); charSet.add('!'); } @Override public String name() { return "compare"; } /** * 支持 {@code > < = >= <= !} 后面加数字 * * @param targetStr * @param patternStr * @return */ @Override public boolean match(String targetStr, String patternStr) { char[] chars = patternStr.toCharArray(); if (isLegalChar(chars[0]) && isLegalChar(chars[1])) { return process(targetStr, patternStr.substring(0, 2), patternStr.substring(2)); } else if (isLegalChar(chars[0])) { return process(targetStr, patternStr.substring(0, 1), patternStr.substring(1)); } else { throw new IllegalArgsOperatorException("operator " + patternStr + " is illegal."); } } private boolean process(String targetStr, String charStr, String numStr) { double result; double target; try { target = Double.parseDouble(targetStr); if (numStr.startsWith("-")) { result = -Double.parseDouble(numStr.substring(1)); } else { result = Double.parseDouble(numStr); } } catch (NumberFormatException e) { throw new IllegalArgsOperatorException("operator " + charStr + numStr + " is illegal."); } switch (charStr) { case ">": return target > result; case "<": return target < result; case "=": return doubleEquals(target, result); case ">=": return target >= result; case "<=": return target <= result; case "!": case "!=": return !doubleEquals(target, result); default: throw new IllegalArgsOperatorException("operator " + charStr + numStr + " is illegal."); } } private boolean isLegalChar(char c) { return charSet.contains(c); } private boolean doubleEquals(double target, double result) { return Math.abs(target - result) < 1e-6; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; public class ContainsOperator implements MatchOperator { @Override public String name() { return "contains"; } @Override public boolean match(String targetStr, String patternStr) { return targetStr.contains(patternStr); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; import org.apache.commons.lang3.StringUtils; public class ExactOperator implements MatchOperator { @Override public String name() { return "exact"; } @Override public boolean match(String targetStr, String patternStr) { return StringUtils.equals(targetStr, patternStr); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; public interface MatchOperator { String name(); boolean match(String targetStr, String patternStr); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; import org.apache.commons.lang3.StringUtils; public class PrefixOperator implements MatchOperator { @Override public String name() { return "prefix"; } @Override public boolean match(String requestValue, String patternValue) { return StringUtils.startsWith(requestValue, patternValue); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/RawOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; import java.util.HashMap; public class RawOperator extends HashMap { private static final long serialVersionUID = 659728839992490564L; } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.marker.operator; import org.apache.commons.lang3.StringUtils; public class SuffixOperator implements MatchOperator { @Override public String name() { return "suffix"; } @Override public boolean match(String requestValue, String patternValue) { return StringUtils.endsWith(requestValue, patternValue); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/AbstractPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.entity.Configurable; import org.apache.servicecomb.governance.utils.GovernanceUtils; public abstract class AbstractPolicy extends Configurable implements Comparable { protected int order = 0; @Override public boolean isValid() { return !StringUtils.isEmpty(name); } @Override public int compareTo(AbstractPolicy o) { return this.order - o.order; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } private Duration parseToDuration(String time, Duration defaultValue) { if (StringUtils.isEmpty(time)) { return defaultValue; } if (time.matches(GovernanceUtils.DIGIT_REGEX)) { if (Long.parseLong(time) < 0) { throw new RuntimeException("The value of time should not be less than 0."); } return Duration.ofMillis(Long.parseLong(time)); } return Duration.parse(GovernanceUtils.DIGIT_PREFIX + time); } public String stringOfDuration(String time, Duration defaultValue) { return parseToDuration(time, defaultValue).toString(); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/BulkheadPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; public class BulkheadPolicy extends AbstractPolicy { public static final int DEFAULT_MAX_CONCURRENT_CALLS = 1000; public static final Duration DEFAULT_MAX_WAIT_DURATION = Duration.ofMillis(0); private int maxConcurrentCalls = DEFAULT_MAX_CONCURRENT_CALLS; private String maxWaitDuration = DEFAULT_MAX_WAIT_DURATION.toString(); public int getMaxConcurrentCalls() { return maxConcurrentCalls; } public void setMaxConcurrentCalls(int maxConcurrentCalls) { this.maxConcurrentCalls = maxConcurrentCalls; } public String getMaxWaitDuration() { return maxWaitDuration; } public void setMaxWaitDuration(String maxWaitDuration) { this.maxWaitDuration = stringOfDuration(maxWaitDuration, DEFAULT_MAX_WAIT_DURATION); } @Override public boolean isValid() { if (maxConcurrentCalls < 0) { return false; } if (Duration.parse(maxWaitDuration).toMillis() < 0) { return false; } return super.isValid(); } @Override public String toString() { return "BulkheadPolicy{" + "maxConcurrentCalls=" + maxConcurrentCalls + ", maxWaitDuration=" + maxWaitDuration + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/CircuitBreakerPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.utils.GovernanceUtils; import org.springframework.util.CollectionUtils; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType; public class CircuitBreakerPolicy extends AbstractPolicy { public static final float DEFAULT_FAILURE_RATE_THRESHOLD = 50; public static final float DEFAULT_SLOW_CALL_RATE_THRESHOLD = 100; public static final Duration DEFAULT_WAIT_DURATION_IN_OPEN_STATUS = Duration.ofMillis(60000); //ms public static final Duration DEFAULT_SLOW_CALL_DURATION_THRESHOLD = Duration.ofMillis(60000); // the number of permitted calls when the CircuitBreaker is half open. public static final int DEFAULT_PERMITTED = 10; public static final int DEFAULT_MINIMUM_NUMBER_CALLS = 100; public static final String DEFAULT_SLIDING_WINDOW_SIZE = "100"; public static final String DEFAULT_FAILURE_RESPONSE_STATUS_502 = "502"; public static final String DEFAULT_FAILURE_RESPONSE_STATUS_503 = "503"; public static final List DEFAULT_STATUS_LIST = Arrays.asList(DEFAULT_FAILURE_RESPONSE_STATUS_502, DEFAULT_FAILURE_RESPONSE_STATUS_503); private float failureRateThreshold = DEFAULT_FAILURE_RATE_THRESHOLD; private float slowCallRateThreshold = DEFAULT_SLOW_CALL_RATE_THRESHOLD; private String waitDurationInOpenState = DEFAULT_WAIT_DURATION_IN_OPEN_STATUS.toString(); private String slowCallDurationThreshold = DEFAULT_SLOW_CALL_DURATION_THRESHOLD.toString(); private int permittedNumberOfCallsInHalfOpenState = DEFAULT_PERMITTED; private int minimumNumberOfCalls = DEFAULT_MINIMUM_NUMBER_CALLS; private String slidingWindowType; private String slidingWindowSize = DEFAULT_SLIDING_WINDOW_SIZE; //status code that need record as a failure private List recordFailureStatus = DEFAULT_STATUS_LIST; //force close this circuit breaker. This parameter is not used by circuit breaker directly private boolean forceClosed = false; //force open this circuit breaker. This parameter is not used by circuit breaker directly private boolean forceOpen = false; public CircuitBreakerPolicy() { } @Override public boolean isValid() { if (failureRateThreshold > 100.0F || failureRateThreshold <= 0.0F) { return false; } if (slowCallRateThreshold > 100.0F || slowCallRateThreshold <= 0.0F) { return false; } if (Duration.parse(waitDurationInOpenState).toMillis() <= 0) { return false; } if (Duration.parse(slowCallDurationThreshold).toMillis() <= 0) { return false; } if (permittedNumberOfCallsInHalfOpenState <= 0) { return false; } if (minimumNumberOfCalls <= 0) { return false; } return super.isValid(); } public float getFailureRateThreshold() { return failureRateThreshold; } public void setFailureRateThreshold(float failureRateThreshold) { this.failureRateThreshold = failureRateThreshold; } public float getSlowCallRateThreshold() { return slowCallRateThreshold; } public void setSlowCallRateThreshold(float slowCallRateThreshold) { this.slowCallRateThreshold = slowCallRateThreshold; } public String getWaitDurationInOpenState() { return waitDurationInOpenState; } public void setWaitDurationInOpenState(String waitDurationInOpenState) { this.waitDurationInOpenState = stringOfDuration(waitDurationInOpenState, DEFAULT_WAIT_DURATION_IN_OPEN_STATUS); } public String getSlowCallDurationThreshold() { return slowCallDurationThreshold; } public void setSlowCallDurationThreshold(String slowCallDurationThreshold) { this.slowCallDurationThreshold = stringOfDuration(slowCallDurationThreshold, DEFAULT_SLOW_CALL_DURATION_THRESHOLD); } public int getPermittedNumberOfCallsInHalfOpenState() { return permittedNumberOfCallsInHalfOpenState; } public void setPermittedNumberOfCallsInHalfOpenState(int permittedNumberOfCallsInHalfOpenState) { this.permittedNumberOfCallsInHalfOpenState = permittedNumberOfCallsInHalfOpenState; } public int getMinimumNumberOfCalls() { return minimumNumberOfCalls; } public void setMinimumNumberOfCalls(int minimumNumberOfCalls) { this.minimumNumberOfCalls = minimumNumberOfCalls; } public SlidingWindowType getSlidingWindowTypeEnum() { if (StringUtils.isEmpty(slidingWindowType)) { return SlidingWindowType.TIME_BASED; } try { return SlidingWindowType.valueOf(slidingWindowType); } catch (Exception e) { return SlidingWindowType.TIME_BASED; } } public String getSlidingWindowType() { return this.slidingWindowType; } public void setSlidingWindowType(String slidingWindowType) { this.slidingWindowType = slidingWindowType; } // time's unit is second public String getSlidingWindowSize() { return slidingWindowSize; } public void setSlidingWindowSize(String slidingWindowSize) { this.slidingWindowSize = getValue(slidingWindowSize); } private String getValue(String slidingWindowSize) { if (StringUtils.isEmpty(slidingWindowSize)) { return DEFAULT_SLIDING_WINDOW_SIZE; } if (slidingWindowSize.matches(GovernanceUtils.DIGIT_REGEX)) { if (Long.parseLong(slidingWindowSize) < 0) { throw new RuntimeException("The value should be more than 0."); } return slidingWindowSize; } Duration duration = Duration.parse(GovernanceUtils.DIGIT_PREFIX + slidingWindowSize); return String.valueOf(duration.getSeconds()); } public List getRecordFailureStatus() { if (CollectionUtils.isEmpty(this.recordFailureStatus)) { return DEFAULT_STATUS_LIST; } return this.recordFailureStatus; } public void setRecordFailureStatus(List recordFailureStatus) { if (recordFailureStatus == null) { return; } this.recordFailureStatus = recordFailureStatus.stream().filter(e -> !StringUtils.isEmpty(e)) .collect(Collectors.toList()); } public boolean isForceClosed() { return forceClosed; } public void setForceClosed(boolean forceClosed) { this.forceClosed = forceClosed; } public boolean isForceOpen() { return forceOpen; } public void setForceOpen(boolean forceOpen) { this.forceOpen = forceOpen; } @Override public String toString() { return "CircuitBreakerPolicy{" + "failureRateThreshold=" + failureRateThreshold + ", slowCallRateThreshold=" + slowCallRateThreshold + ", waitDurationInOpenState=" + waitDurationInOpenState + ", slowCallDurationThreshold=" + slowCallDurationThreshold + ", permittedNumberOfCallsInHalfOpenState=" + permittedNumberOfCallsInHalfOpenState + ", minimumNumberOfCalls=" + minimumNumberOfCalls + ", slidingWindowType='" + slidingWindowType + '\'' + ", slidingWindowSize=" + slidingWindowSize + ", recordFailureStatus=" + recordFailureStatus + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; import org.apache.servicecomb.governance.processor.injection.FaultInjectionConst; public class FaultInjectionPolicy extends AbstractPolicy { public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(0); private String type = FaultInjectionConst.TYPE_DELAY; private String delayTime = DEFAULT_TIMEOUT_DURATION.toString(); private int percentage = -1; private int errorCode = 500; private boolean forceClosed = false; private String fallbackType = FaultInjectionConst.FALLBACK_THROWEXCEPTION; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDelayTime() { return delayTime; } public void setDelayTime(String delayTime) { this.delayTime = stringOfDuration(delayTime, Duration.ofMillis(-1)); } public int getPercentage() { return percentage; } public void setPercentage(int percentage) { this.percentage = percentage; } public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public long getDelayTimeToMillis() { return Duration.parse(delayTime).toMillis(); } public boolean isForceClosed() { return forceClosed; } public void setForceClosed(boolean forceClosed) { this.forceClosed = forceClosed; } public String getFallbackType() { return fallbackType; } public void setFallbackType(String fallbackType) { this.fallbackType = fallbackType; } @Override public boolean isValid() { if (getDelayTimeToMillis() < 0 && FaultInjectionConst.TYPE_DELAY.equals(type)) { return false; } if ((getErrorCode() < FaultInjectionConst.ERROR_CODE_MIN || getErrorCode() > FaultInjectionConst.ERROR_CODE_MAX) && FaultInjectionConst.TYPE_ABORT.equals(type)) { return false; } return super.isValid(); } @Override public String toString() { return "FaultInjectionPolicy{" + "type=" + type + ", delayTime=" + delayTime + ", percentage=" + percentage + ", errorCode=" + errorCode + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/GovernanceCachePolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; public class GovernanceCachePolicy extends AbstractPolicy { public static final Duration DEFAULT_TTL = Duration.ofMillis(21600000); public static final long DEFAULT_MAXIMUM_SIZE = 60000; public static final int DEFAULT_CONCURRENCY_LEVEL = 8; private String ttl = DEFAULT_TTL.toString(); private long maximumSize = DEFAULT_MAXIMUM_SIZE; private int concurrencyLevel = DEFAULT_CONCURRENCY_LEVEL; public String getTtl() { return ttl; } public void setTtl(String ttl) { this.ttl = stringOfDuration(ttl, DEFAULT_TTL); } public Long getMaximumSize() { return maximumSize; } public void setMaximumSize(Long maximumSize) { this.maximumSize = maximumSize; } public int getConcurrencyLevel() { return concurrencyLevel; } public void setConcurrencyLevel(int concurrencyLevel) { this.concurrencyLevel = concurrencyLevel; } @Override public String toString() { return "CachePolicy{" + "ttl=" + ttl + ",concurrencyLevel=" + concurrencyLevel + ", maximumSize=" + maximumSize + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/IdentifierRateLimitingPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import org.apache.commons.lang3.StringUtils; public class IdentifierRateLimitingPolicy extends RateLimitingPolicy { /** * header name used to identify this rate limiter */ protected String identifier; public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } @Override public boolean isValid() { if (StringUtils.isEmpty(identifier)) { return false; } return super.isValid(); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/LoadBalancerPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; public class LoadBalancerPolicy extends AbstractPolicy { private String rule; public String getRule() { return rule; } public void setRule(String rule) { this.rule = rule; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/MapperPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.util.Map; public class MapperPolicy extends AbstractPolicy { private Map target; public Map getTarget() { return target; } public void setTarget(Map target) { this.target = target; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/RateLimitingPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; public class RateLimitingPolicy extends AbstractPolicy { public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(0); public static final Duration DEFAULT_LIMIT_REFRESH_PERIOD = Duration.ofMillis(1000); public static final int DEFAULT_LIMIT_FOR_PERIOD = 1000; protected String timeoutDuration = DEFAULT_TIMEOUT_DURATION.toString(); protected String limitRefreshPeriod = DEFAULT_LIMIT_REFRESH_PERIOD.toString(); // 配置项名称使用 rate, 对应于 resilience4j 的 limitForPeriod protected int rate = DEFAULT_LIMIT_FOR_PERIOD; public String getTimeoutDuration() { return timeoutDuration; } public void setTimeoutDuration(String timeoutDuration) { this.timeoutDuration = stringOfDuration(timeoutDuration, DEFAULT_TIMEOUT_DURATION); } public String getLimitRefreshPeriod() { return limitRefreshPeriod; } public void setLimitRefreshPeriod(String limitRefreshPeriod) { this.limitRefreshPeriod = stringOfDuration(limitRefreshPeriod, DEFAULT_LIMIT_REFRESH_PERIOD); } public int getRate() { return rate; } public void setRate(int rate) { this.rate = rate; } public RateLimitingPolicy() { } @Override public boolean isValid() { if (Duration.parse(timeoutDuration).toMillis() < 0) { return false; } if (Duration.parse(limitRefreshPeriod).toMillis() <= 0) { return false; } if (rate <= 0) { return false; } return super.isValid(); } @Override public String toString() { return "RateLimitingPolicy{" + "timeoutDuration=" + timeoutDuration + ", limitRefreshPeriod=" + limitRefreshPeriod + ", rate=" + rate + " req/s" + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/RetryPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.springframework.util.CollectionUtils; public class RetryPolicy extends AbstractPolicy { public static final int DEFAULT_MAX_ATTEMPTS = 3; public static final Duration DEFAULT_WAIT_DURATION = Duration.ofMillis(1); public static final String DEFAULT_RETRY_ON_RESPONSE_STATUS_502 = "502"; public static final String DEFAULT_RETRY_ON_RESPONSE_STATUS_503 = "503"; public static final List DEFAULT_STATUS_LIST = Arrays.asList(DEFAULT_RETRY_ON_RESPONSE_STATUS_502, DEFAULT_RETRY_ON_RESPONSE_STATUS_503); private static final Duration INITIAL_INTERVAL = Duration.ofMillis(1000); private static final float MULTIPLIER = 2; private static final double RANDOMIZATION_FACTOR = 0.5; private static final String DEFAULT_RETRY_STRATEGY = "FixedInterval"; //max retry attempts private int maxAttempts = DEFAULT_MAX_ATTEMPTS; //wait duration for each retry private String waitDuration = DEFAULT_WAIT_DURATION.toString(); //status code that need retry private List retryOnResponseStatus = DEFAULT_STATUS_LIST; //retry strategy private String retryStrategy = DEFAULT_RETRY_STRATEGY; // initial interval for backoff retry private String initialInterval = INITIAL_INTERVAL.toString(); // multiplier for backoff retry private float multiplier = MULTIPLIER; // randomization factor for backoff retry private double randomizationFactor = RANDOMIZATION_FACTOR; // if throw an MaxRetriesExceededException if retry condition is based on result private boolean failAfterMaxAttempts = false; // if retry on the same instance. This property is not directly used in // RetryHandler, but used for loadbalancers private int retryOnSame = 0; public List getRetryOnResponseStatus() { if (CollectionUtils.isEmpty(retryOnResponseStatus)) { return DEFAULT_STATUS_LIST; } return retryOnResponseStatus; } public void setRetryOnResponseStatus(List retryOnResponseStatus) { if (retryOnResponseStatus == null) { return; } this.retryOnResponseStatus = retryOnResponseStatus.stream().filter(e -> !StringUtils.isEmpty(e)) .collect(Collectors.toList()); } public int getMaxAttempts() { return maxAttempts; } public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; } public String getWaitDuration() { return Duration.parse(waitDuration).toMillis() < 1 ? DEFAULT_WAIT_DURATION.toString() : waitDuration; } public void setWaitDuration(String waitDuration) { this.waitDuration = stringOfDuration(waitDuration, DEFAULT_WAIT_DURATION); } public String getRetryStrategy() { if (StringUtils.isEmpty(retryStrategy)) { retryStrategy = DEFAULT_RETRY_STRATEGY; } return retryStrategy; } public void setRetryStrategy(String retryStrategy) { this.retryStrategy = retryStrategy; } public String getInitialInterval() { return initialInterval; } public void setInitialInterval(String initialInterval) { this.initialInterval = stringOfDuration(initialInterval, INITIAL_INTERVAL); } public float getMultiplier() { return multiplier; } public void setMultiplier(float multiplier) { this.multiplier = multiplier; } public double getRandomizationFactor() { return randomizationFactor; } public void setRandomizationFactor(double randomizationFactor) { this.randomizationFactor = randomizationFactor; } public boolean isFailAfterMaxAttempts() { return failAfterMaxAttempts; } public void setFailAfterMaxAttempts(boolean failAfterMaxAttempts) { this.failAfterMaxAttempts = failAfterMaxAttempts; } public int getRetryOnSame() { return retryOnSame; } public void setRetryOnSame(int retryOnSame) { this.retryOnSame = retryOnSame; } @Override public boolean isValid() { if (maxAttempts < 1) { return false; } if (Duration.parse(waitDuration).toMillis() < 0) { return false; } if (Duration.parse(initialInterval).toMillis() < 10) { return false; } return super.isValid(); } @Override public String toString() { return "RetryPolicy{" + "maxAttempts=" + maxAttempts + ", waitDuration=" + waitDuration + ", retryOnResponseStatus='" + retryOnResponseStatus + '\'' + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/policy/TimeLimiterPolicy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.policy; import java.time.Duration; public class TimeLimiterPolicy extends AbstractPolicy { public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(1000); public static final boolean DEFAULT_CANCEL_RUNNING_FUTURE = true; private String timeoutDuration = DEFAULT_TIMEOUT_DURATION.toString(); private boolean cancelRunningFuture = DEFAULT_CANCEL_RUNNING_FUTURE; public String getTimeoutDuration() { return timeoutDuration; } public void setTimeoutDuration(String timeoutDuration) { this.timeoutDuration = stringOfDuration(timeoutDuration, DEFAULT_TIMEOUT_DURATION); } public boolean isCancelRunningFuture() { return cancelRunningFuture; } public void setCancelRunningFuture(boolean cancelRunningFuture) { this.cancelRunningFuture = cancelRunningFuture; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbortFault.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AbortFault extends AbstractFault { private static final Logger LOGGER = LoggerFactory.getLogger(AbortFault.class); public static final String ABORTED_ERROR_MSG = "aborted by fault inject"; public AbortFault(String key, FaultInjectionPolicy policy) { super(key, policy); } @Override public boolean injectFault(FaultParam faultParam) { return shouldAbort(faultParam, policy); } private boolean shouldAbort(FaultParam param, FaultInjectionPolicy policy) { // get the config values related to abort. int abortPercent = policy.getPercentage(); if (abortPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: Abort percentage is not configured"); return false; } // check fault abort condition. return FaultInjectionUtil.isFaultNeedToInject(param.getReqCount(), abortPercent); } @Override public int getOrder() { return 200; } @Override public String getName() { return FaultInjectionConst.TYPE_ABORT; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbstractFault.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; public abstract class AbstractFault implements Fault { protected String key; protected FaultInjectionPolicy policy; public AbstractFault(String key, FaultInjectionPolicy policy) { this.key = key; this.policy = policy; } @Override public boolean injectFault() { if (policy.isForceClosed()) { return false; } FaultParam faultParam = FaultInjectionUtil.initFaultParam(key); return injectFault(faultParam); } @Override public String getKey() { return key; } @Override public FaultInjectionPolicy getPolicy() { return policy; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/DelayFault.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DelayFault extends AbstractFault { private static final Logger LOGGER = LoggerFactory.getLogger(DelayFault.class); public DelayFault(String key, FaultInjectionPolicy policy) { super(key, policy); } @Override public int getOrder() { return 100; } @Override public boolean injectFault(FaultParam faultParam) { if (!shouldDelay(faultParam, policy)) { return false; } LOGGER.debug("Fault injection: delay is added for the request by fault inject handler"); long delay = policy.getDelayTimeToMillis(); if (delay == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: delay is not configured"); return false; } executeDelay(faultParam, delay); return false; } private void executeDelay(FaultParam faultParam, long delay) { Sleepable sleepable = faultParam.getSleepable(); if (sleepable != null) { sleepable.sleep(delay); } } private boolean shouldDelay(FaultParam param, FaultInjectionPolicy policy) { int delayPercent = policy.getPercentage(); if (delayPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: delay percentage is not configured"); return false; } // check fault delay condition. return FaultInjectionUtil.isFaultNeedToInject(param.getReqCount(), delayPercent); } @Override public String getName() { return FaultInjectionConst.TYPE_DELAY; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Fault.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; import io.vavr.CheckedFunction0; public interface Fault { static CheckedFunction0 decorateCheckedSupplier(Fault fault, CheckedFunction0 supplier) { return () -> { if (fault.injectFault()) { if (FaultInjectionConst.FALLBACK_THROWEXCEPTION.equals(fault.getPolicy().getFallbackType())) { throw new FaultInjectionException( FaultResponse.createFail(fault.getPolicy().getErrorCode(), AbortFault.ABORTED_ERROR_MSG)); } else { return null; } } return supplier.apply(); }; } int getOrder(); String getName(); /* * If true is returned,the downgrade governance policy is executed. * Otherwise,the original request is directly executed. * */ boolean injectFault(); /* * If true is returned,the downgrade governance policy is executed. * Otherwise,the original request is directly executed. * */ boolean injectFault(FaultParam faultParam); String getKey(); FaultInjectionPolicy getPolicy(); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; /** * Handles the all constant values for fault injection. */ public class FaultInjectionConst { public static final int FAULT_INJECTION_DEFAULT_VALUE = -1; public static final String TYPE_DELAY = "delay"; public static final String TYPE_ABORT = "abort"; public static final String FALLBACK_THROWEXCEPTION = "ThrowException"; public static final String FALLBACK_RETURNNULL = "ReturnNull"; public static final int ERROR_CODE_MIN = 200; public static final int ERROR_CODE_MAX = 600; } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionDecorators.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import io.vavr.CheckedFunction0; public interface FaultInjectionDecorators { static FaultInjectionDecorateCheckedSupplier ofCheckedSupplier(CheckedFunction0 supplier) { return new FaultInjectionDecorateCheckedSupplier<>(supplier); } class FaultInjectionDecorateCheckedSupplier { private CheckedFunction0 supplier; protected FaultInjectionDecorateCheckedSupplier(CheckedFunction0 supplier) { this.supplier = supplier; } public FaultInjectionDecorateCheckedSupplier withFaultInjection(Fault fault) { supplier = Fault.decorateCheckedSupplier(fault, supplier); return this; } public T get() throws Throwable { return supplier.apply(); } } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; public class FaultInjectionException extends RuntimeException { private static final long serialVersionUID = 1675558351029273343L; private final FaultResponse faultResponse; public FaultInjectionException(FaultResponse faultResponse) { super(faultResponse.getErrorMsg()); this.faultResponse = faultResponse; } public FaultResponse getFaultResponse() { return faultResponse; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; import io.vertx.core.Context; import io.vertx.core.Vertx; /** * Handles the count for all request based key[transport + microservice qualified name]. */ public final class FaultInjectionUtil { private FaultInjectionUtil() { } /** * key is transport+operQualifiedName */ private static final Map REQUEST_COUNT = new ConcurrentHashMapEx<>(); /** * Returns total requests per provider for operational level. * * @param key * transport+operational name * @return long total requests */ public static AtomicLong getOperMetTotalReq(String key) { return REQUEST_COUNT.computeIfAbsent(key, p -> new AtomicLong(1)); } /** * It will check the delay/abort condition based on request count and percentage * received. * * @param reqCount total request count of the uri * @param percentage the percentage of hitting fault injection * @return true: delay/abort is needed. false: delay/abort is not needed. */ public static boolean isFaultNeedToInject(long reqCount, int percentage) { /* * Example: delay/abort percentage configured is 10% and Get the count(suppose * if it is 10th request) from map and calculate resultNew(10th request) and * requestOld(9th request). Like this for every request it will calculate * current request count and previous count. if both not matched need to add * delay/abort otherwise no need to add. */ // calculate the value with current request count. long resultNew = (reqCount * percentage) / 100; // calculate the value with previous count value. long resultOld = ((reqCount - 1) * percentage) / 100; // if both are not matching then delay/abort should be added. return (resultNew != resultOld); } public static Fault getFault(String key, FaultInjectionPolicy policy) { Fault fault = null; if (FaultInjectionConst.TYPE_DELAY.equals(policy.getType())) { fault = new DelayFault(key, policy); } else if (FaultInjectionConst.TYPE_ABORT.equals(policy.getType())) { fault = new AbortFault(key, policy); } return fault; } public static FaultParam initFaultParam(String key) { AtomicLong reqCount = FaultInjectionUtil.getOperMetTotalReq(key); // increment the request count here after checking the delay/abort condition. long reqCountCurrent = reqCount.getAndIncrement(); FaultParam param = new FaultParam(reqCountCurrent); Context currentContext = Vertx.currentContext(); if (currentContext != null && currentContext.owner() != null && currentContext.isEventLoopContext()) { param.setSleepable( (delay) -> currentContext.owner().setTimer(delay, timeId -> {})); } return param; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Fault injection parameters which decides the fault injection condition. */ public class FaultParam { private static final Logger LOGGER = LoggerFactory.getLogger(FaultParam.class); private final long reqCount; private Sleepable sleepable = (delay) -> { try { Thread.sleep(delay); } catch (InterruptedException e) { LOGGER.info("Interrupted exception is received"); } }; public long getReqCount() { return reqCount; } public FaultParam(long reqCount) { this.reqCount = reqCount; } public Sleepable getSleepable() { return sleepable; } public void setSleepable(Sleepable sleepable) { this.sleepable = sleepable; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; public class FaultResponse { private int errorCode; private String errorMsg; public static FaultResponse createFail(int errorCode, String errorMsg) { FaultResponse faultResponse = new FaultResponse(); faultResponse.setErrorCode(errorCode); faultResponse.setErrorMsg(errorMsg); return faultResponse; } public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Sleepable.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.injection; public interface Sleepable { /** * sleep some time * @param delay time unit is millisecond */ void sleep(long delay); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.loadbanlance; import org.apache.servicecomb.governance.policy.LoadBalancerPolicy; public interface LoadBalance { static LoadBalance getLoadBalance(String key, LoadBalancerPolicy policy) { LoadBalance loadBalance = new LoadBalanceImpl(policy.getRule()); return loadBalance; } String getRule(); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalanceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.loadbanlance; public class LoadBalanceImpl implements LoadBalance { private final String rule; public LoadBalanceImpl(String rule) { this.rule = rule; } public String getRule() { return rule; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/processor/mapping/Mapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.processor.mapping; import java.util.Map; public interface Mapper { static Mapper create(Map target) { return () -> target; } Map target(); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/BulkheadProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.BulkheadPolicy; public class BulkheadProperties extends PolicyProperties { public static final String MATCH_BULKHEAD_KEY = "servicecomb.bulkhead"; public BulkheadProperties() { super(MATCH_BULKHEAD_KEY); } public BulkheadProperties(String key) { super(key); } @Override public Class getEntityClass() { return BulkheadPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/CircuitBreakerProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; public class CircuitBreakerProperties extends PolicyProperties { public static final String MATCH_CIRCUITBREAKER_KEY = "servicecomb.circuitBreaker"; public CircuitBreakerProperties() { super(MATCH_CIRCUITBREAKER_KEY); } public CircuitBreakerProperties(String key) { super(key); } @Override public Class getEntityClass() { return CircuitBreakerPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; public class FaultInjectionProperties extends PolicyProperties { public static final String MATCH_FAULT_INJECTION_KEY = "servicecomb.faultInjection"; public FaultInjectionProperties() { super(MATCH_FAULT_INJECTION_KEY); } public FaultInjectionProperties(String key) { super(key); } @Override public Class getEntityClass() { return FaultInjectionPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceCacheProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; public class GovernanceCacheProperties extends PolicyProperties { public static final String MATCH_CACHE_KEY = "servicecomb.cache"; public GovernanceCacheProperties() { super(MATCH_CACHE_KEY); } public GovernanceCacheProperties(String key) { super(key); } @Override public Class getEntityClass() { return GovernanceCachePolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.MicroserviceMeta; import org.apache.servicecomb.governance.entity.Configurable; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.util.CollectionUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.TypeDescription; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.representer.Representer; import com.google.common.eventbus.Subscribe; public abstract class GovernanceProperties implements InitializingBean { private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceProperties.class); private final Representer representer = new Representer(new DumperOptions()); private final String configKey; protected Environment environment; private MicroserviceMeta microserviceMeta; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setMicroserviceMeta(MicroserviceMeta microserviceMeta) { this.microserviceMeta = microserviceMeta; } protected Map parsedEntity; protected Class entityClass; protected GovernanceProperties(String key) { configKey = key; representer.getPropertyUtils().setSkipMissingProperties(true); GovernanceEventManager.register(this); entityClass = getEntityClass(); } public String getConfigKey() { return this.configKey; } @Override public void afterPropertiesSet() { parsedEntity = parseEntity(readPropertiesFromPrefix()); } @Subscribe public void onConfigurationChangedEvent(GovernanceConfigurationChangedEvent event) { for (String key : event.getChangedConfigurations()) { if (key.startsWith(configKey + ".")) { String mapKey = key.substring((configKey + ".").length()); parsedEntity.remove(mapKey); T entityItem = parseEntityItem(mapKey, environment.getProperty(key)); if (entityItem != null) { parsedEntity.put(mapKey, entityItem); } } } } private Map readPropertiesFromPrefix() { Set allKeys = getAllKeys(environment); Map result = new HashMap<>(); allKeys.forEach(key -> { if (key.startsWith(configKey + ".")) { result.put(key.substring(configKey.length() + 1), environment.getProperty(key)); } }); return result; } private Set getAllKeys(Environment environment) { Set allKeys = new HashSet<>(); if (!(environment instanceof ConfigurableEnvironment)) { return allKeys; } ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; for (PropertySource propertySource : configurableEnvironment.getPropertySources()) { getProperties(propertySource, allKeys); } return allKeys; } private void getProperties(PropertySource propertySource, Set allKeys) { if (propertySource instanceof CompositePropertySource) { // recursively get EnumerablePropertySource CompositePropertySource compositePropertySource = (CompositePropertySource) propertySource; compositePropertySource.getPropertySources().forEach(ps -> getProperties(ps, allKeys)); return; } if (propertySource instanceof EnumerablePropertySource) { EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; Collections.addAll(allKeys, enumerablePropertySource.getPropertyNames()); return; } LOGGER.debug("None EnumerablePropertySource ignored in {}, propertySourceName = [{}]", this.getClass().getName(), propertySource.getName()); } public Map getParsedEntity() { return this.parsedEntity; } protected Map parseEntity(Map yamlEntity) { if (CollectionUtils.isEmpty(yamlEntity)) { return new HashMap<>(); } Map resultMap = new HashMap<>(); for (Entry entry : yamlEntity.entrySet()) { T marker = parseEntityItem(entry.getKey(), entry.getValue()); if (marker != null) { resultMap.put(entry.getKey(), marker); } } return resultMap; } protected abstract Class getEntityClass(); protected T parseEntityItem(String key, String value) { if (StringUtils.isEmpty(value)) { return null; } try { Yaml entityParser = new Yaml(new Constructor(new TypeDescription(entityClass, entityClass), new LoaderOptions()), representer); T result = entityParser.loadAs(value, entityClass); result.setName(key); if (!result.isValid()) { LOGGER.warn("Entity configuration is not valid and ignored. Key [{}], value [{}]", key, value); return null; } if (!servicesMatch(result.getServices())) { LOGGER.info("Configuration belongs to other service is ignored. Key [{}]", key); return null; } return result; } catch (RuntimeException e) { LOGGER.error("governance config yaml is illegal : {}", e.getMessage()); } return null; } private boolean servicesMatch(String services) { if (StringUtils.isEmpty(services)) { return true; } return Arrays.stream(services.split(",")).anyMatch(service -> { String[] serviceAndVersion = service.split(":"); if (serviceAndVersion.length == 1) { return microserviceMeta.getName().equals(serviceAndVersion[0]); } else if (serviceAndVersion.length == 2) { return microserviceMeta.getName().equals(serviceAndVersion[0]) && microserviceMeta.getVersion() .equals(serviceAndVersion[1]); } else { return false; } }); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/IdentifierRateLimitProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.IdentifierRateLimitingPolicy; public class IdentifierRateLimitProperties extends PolicyProperties { public static final String MATCH_RATE_LIMIT_KEY = "servicecomb.identifierRateLimiting"; public IdentifierRateLimitProperties() { super(MATCH_RATE_LIMIT_KEY); } public IdentifierRateLimitProperties(String key) { super(key); } @Override public Class getEntityClass() { return IdentifierRateLimitingPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceBulkheadProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.BulkheadPolicy; public class InstanceBulkheadProperties extends PolicyProperties { public static final String MATCH_INSTANCE_BULKHEAD_KEY = "servicecomb.instanceBulkhead"; public InstanceBulkheadProperties() { super(MATCH_INSTANCE_BULKHEAD_KEY); } public InstanceBulkheadProperties(String key) { super(key); } @Override public Class getEntityClass() { return BulkheadPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceIsolationProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; public class InstanceIsolationProperties extends PolicyProperties { public static final String MATCH_INSTANCE_ISOLATION_KEY = "servicecomb.instanceIsolation"; public InstanceIsolationProperties() { super(MATCH_INSTANCE_ISOLATION_KEY); } public InstanceIsolationProperties(String key) { super(key); } @Override public Class getEntityClass() { return CircuitBreakerPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/LoadBalanceProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.LoadBalancerPolicy; public class LoadBalanceProperties extends PolicyProperties { public static final String MATCH_LOADBANLANCER_KEY = "servicecomb.loadbalance"; public LoadBalanceProperties() { super(MATCH_LOADBANLANCER_KEY); } public LoadBalanceProperties(String key) { super(key); } @Override protected Class getEntityClass() { return LoadBalancerPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/MapperProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.MapperPolicy; public class MapperProperties extends PolicyProperties { public static final String MATCH_MAPPER_KEY = "servicecomb.mapper"; public MapperProperties() { super(MATCH_MAPPER_KEY); } public MapperProperties(String key) { super(key); } @Override protected Class getEntityClass() { return MapperPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/MatchProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.marker.TrafficMarker; public class MatchProperties extends GovernanceProperties { public static final String MATCH_POLICY_KEY = "servicecomb.matchGroup"; public MatchProperties() { super(MATCH_POLICY_KEY); } @Override public Class getEntityClass() { return TrafficMarker.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/PolicyProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.AbstractPolicy; public abstract class PolicyProperties extends GovernanceProperties { protected PolicyProperties(String key) { super(key); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/RateLimitProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.RateLimitingPolicy; public class RateLimitProperties extends PolicyProperties { public static final String MATCH_RATE_LIMIT_KEY = "servicecomb.rateLimiting"; public RateLimitProperties() { super(MATCH_RATE_LIMIT_KEY); } public RateLimitProperties(String key) { super(key); } @Override public Class getEntityClass() { return RateLimitingPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/RetryProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.RetryPolicy; public class RetryProperties extends PolicyProperties { public static final String MATCH_RETRY_KEY = "servicecomb.retry"; public RetryProperties() { super(MATCH_RETRY_KEY); } public RetryProperties(String key) { super(key); } @Override public Class getEntityClass() { return RetryPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/properties/TimeLimiterProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.properties; import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; public class TimeLimiterProperties extends PolicyProperties { public static final String MATCH_TIMELIMITER_KEY = "servicecomb.timeLimiter"; public TimeLimiterProperties() { super(MATCH_TIMELIMITER_KEY); } public TimeLimiterProperties(String key) { super(key); } @Override public Class getEntityClass() { return TimeLimiterPolicy.class; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.service; import com.google.common.cache.Cache; import java.util.Objects; public interface GovernanceCache { static GovernanceCache of(Cache cache) { Objects.requireNonNull(cache, "Cache must not be null"); return new GovernanceCacheImpl<>(cache); } V getValueFromCache(K cacheKey); void putValueIntoCache(K cacheKey, V value); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCacheImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.service; import com.google.common.cache.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GovernanceCacheImpl implements GovernanceCache { private static final Logger LOG = LoggerFactory.getLogger(GovernanceCacheImpl.class); private final Cache cache; public GovernanceCacheImpl(Cache cache) { this.cache = cache; } public V getValueFromCache(K cacheKey) { try { return cache.getIfPresent(cacheKey); } catch (Exception exception) { LOG.warn("Failed to get a value from Cache", exception); return null; } } @Override public void putValueIntoCache(K cacheKey, V value) { try { cache.put(cacheKey, value); } catch (Exception exception) { LOG.warn("Failed to put a value into Cache {}", exception); } } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/service/MatchersService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.service; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; public interface MatchersService { boolean checkMatch(GovernanceRequestExtractor governanceRequest, String key); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/service/MatchersServiceImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.service; import java.util.Map; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.governance.marker.TrafficMarker; import org.apache.servicecomb.governance.properties.MatchProperties; public class MatchersServiceImpl implements MatchersService { private final RequestProcessor requestProcessor; private final MatchProperties matchProperties; public MatchersServiceImpl(RequestProcessor requestProcessor, MatchProperties matchProperties) { this.requestProcessor = requestProcessor; this.matchProperties = matchProperties; } @Override public boolean checkMatch(GovernanceRequestExtractor governanceRequest, String key) { Map parsedEntity = matchProperties.getParsedEntity(); TrafficMarker trafficMarker = parsedEntity.get(key); if (trafficMarker == null) { return false; } return trafficMarker.checkMatch(governanceRequest, requestProcessor); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/utils/CustomMatch.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.utils; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; public interface CustomMatch { boolean matchRequest(GovernanceRequestExtractor requestExtractor, String parameters); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/governance/utils/GovernanceUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.utils; public final class GovernanceUtils { public static final String DIGIT_REGEX = "-{0,1}[0-9]{1,10}"; public static final String DIGIT_PREFIX = "PT"; public static final String STRATEGY_RANDOM_BACKOFF = "RandomBackoff"; } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.match.RouterRuleMatcher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class RouterCommonConfiguration { @Bean public RouterRuleCache scbRouterRuleCache(Environment environment) { return new RouterRuleCache(environment); } @Bean public RouterRuleMatcher scbRouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { return new RouterRuleMatcher(routerRuleCache, requestProcessor); } @Bean public RouterFilter scbRouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { return new RouterFilter(routerRuleMatcher, routerRuleCache); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.apache.servicecomb.router.match.RouterRuleMatcher; import org.apache.servicecomb.router.model.PolicyRuleItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; public class RouterFilter { private static final Logger LOGGER = LoggerFactory.getLogger(RouterFilter.class); private final RouterRuleMatcher routerRuleMatcher; private final RouterRuleCache routerRuleCache; public RouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { this.routerRuleMatcher = routerRuleMatcher; this.routerRuleCache = routerRuleCache; } public List getFilteredListOfServers(List list, String targetServiceName, GovernanceRequestExtractor extractor, RouterDistributor distributor) { if (CollectionUtils.isEmpty(list)) { return list; } if (StringUtils.isEmpty(targetServiceName)) { return list; } // 1.init and cache if (!routerRuleCache.doInit(targetServiceName)) { LOGGER.debug("route management init failed"); return list; } // 2.match rule PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, extractor); if (invokeRule == null) { LOGGER.debug("route management match rule failed"); return list; } LOGGER.debug("route management match rule success: {}", invokeRule); // 3.distribute select endpoint List resultList = distributor.distribute(targetServiceName, list, invokeRule); LOGGER.debug("route management distribute rule success: {}", resultList); return resultList; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.cache; import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.apache.servicecomb.router.model.PolicyRuleItem; import org.apache.servicecomb.router.model.ServiceInfoCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.TypeDescription; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.representer.Representer; import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; public class RouterRuleCache { private static final Logger LOGGER = LoggerFactory.getLogger(RouterRuleCache.class); public static final String ROUTE_RULE_PREFIX = "servicecomb.routeRule."; private static final String ROUTE_RULE = "servicecomb.routeRule.%s"; public static final String GLOBAL_ROUTE_RULE_KEY = "servicecomb.globalRouteRule"; private final Environment environment; private final ConcurrentHashMap serviceInfoCacheMap = new ConcurrentHashMap<>(); private final Object lock = new Object(); private final Representer representer = new Representer(new DumperOptions()); public RouterRuleCache(Environment environment) { this.environment = environment; representer.getPropertyUtils().setSkipMissingProperties(true); GovernanceEventManager.register(this); } /** * cache and register callback * * return false when: 1. parsing error 2. rule is null */ public boolean doInit(String targetServiceName) { if (!isServerContainRule(targetServiceName)) { return false; } if (!serviceInfoCacheMap.containsKey(targetServiceName)) { synchronized (lock) { if (serviceInfoCacheMap.containsKey(targetServiceName)) { return true; } return addAllRule(targetServiceName); } } return true; } @Subscribe public void onConfigurationChangedEvent(GovernanceConfigurationChangedEvent event) { for (String key : event.getChangedConfigurations()) { if (key.startsWith(ROUTE_RULE_PREFIX)) { serviceInfoCacheMap.remove(key.substring(ROUTE_RULE_PREFIX.length())); } if (key.equals(GLOBAL_ROUTE_RULE_KEY)) { serviceInfoCacheMap.clear(); } } } private boolean addAllRule(String targetServiceName) { String ruleStr = environment.getProperty(String.format(ROUTE_RULE, targetServiceName), ""); if (StringUtils.isEmpty(ruleStr)) { ruleStr = environment.getProperty(GLOBAL_ROUTE_RULE_KEY, ""); } if (StringUtils.isEmpty(ruleStr)) { return false; } List policyRuleItemList; try { Yaml entityParser = new Yaml( new Constructor(new TypeDescription(PolicyRuleItem[].class, PolicyRuleItem[].class), new LoaderOptions()), representer); policyRuleItemList = Arrays .asList(entityParser.loadAs(ruleStr, PolicyRuleItem[].class)); } catch (Exception e) { LOGGER.warn("Route management serialization for service {} failed: {}", targetServiceName, e.getMessage()); return false; } if (CollectionUtils.isEmpty(policyRuleItemList)) { LOGGER.warn("Route management serialization for service {} is empty", targetServiceName); return false; } ServiceInfoCache serviceInfoCache = new ServiceInfoCache(policyRuleItemList); serviceInfoCacheMap.put(targetServiceName, serviceInfoCache); LOGGER.info("Route management serialization service {} rules success, content: {}", targetServiceName, serviceInfoCache.getAllrule()); return true; } /** * if a server don't have rule , avoid registered too many callback , it may cause memory leak */ private boolean isServerContainRule(String targetServiceName) { return !StringUtils.isEmpty(environment.getProperty(String.format(ROUTE_RULE, targetServiceName), "")) || !StringUtils.isEmpty(environment.getProperty(GLOBAL_ROUTE_RULE_KEY, "")); } public ConcurrentHashMap getServiceInfoCacheMap() { return serviceInfoCacheMap; } @VisibleForTesting void refresh() { serviceInfoCacheMap.clear(); } public void refresh(String targetServiceName) { serviceInfoCacheMap.remove(targetServiceName); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.distribute; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.model.PolicyRuleItem; import org.apache.servicecomb.router.model.RouteItem; import org.apache.servicecomb.router.model.TagItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; public abstract class AbstractRouterDistributor implements RouterDistributor { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRouterDistributor.class); private Function getVersion; private Function getServerName; private Function> getProperties; private RouterRuleCache routerRuleCache; @Autowired public void setRouterRuleCache(RouterRuleCache routerRuleCache) { this.routerRuleCache = routerRuleCache; } protected AbstractRouterDistributor() { } /** * distribute logic: * 1、First according to the set route rules to choose target instances, if have just return. * 2、if route rules not match instance, check if fallback rules are set, if set and match instances then return. * 3、if route and fallback routes all have not match instance, then if route rules weight count less 100, return * unset instances, otherwise return all instances. * @param targetServiceName * @param list * @param invokeRule * @return */ @Override public List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule) { invokeRule.check(); // unSetTags instance list List unSetTagInstances = new ArrayList<>(); // record fallback router targItem instance Map> fallbackVersionServerMap = new HashMap<>(); // get tag instance map, fallbackVersionServerMap, unSetTagInstances Map> versionServerMap = getDistributList(targetServiceName, list, invokeRule, unSetTagInstances, fallbackVersionServerMap); // weight calculation to obtain the next tags instance TagItem targetTag = getFilteredServerTagItem(invokeRule, targetServiceName); if (targetTag != null && versionServerMap.containsKey(targetTag)) { return versionServerMap.get(targetTag); } if (!fallbackVersionServerMap.isEmpty()) { // weight calculation to obtain the next fallback tags instance TagItem fallbackTargetTag = getFallbackFilteredServerTagItem(invokeRule, targetServiceName); if (fallbackTargetTag != null && fallbackVersionServerMap.containsKey(fallbackTargetTag)) { return fallbackVersionServerMap.get(fallbackTargetTag); } } // has weightLess situation and unSetTagInstances has values if (invokeRule.isWeightLess() && !unSetTagInstances.isEmpty()) { return unSetTagInstances; } if (invokeRule.isEmptyProtection()) { return list; } // weight set 100 but not matched any instance, then return empty when emptyProtection close return Collections.emptyList(); } @Override public void init(Function getVersion, Function getServerName, Function> getProperties) { this.getVersion = getVersion; this.getServerName = getServerName; this.getProperties = getProperties; } public TagItem getFilteredServerTagItem(PolicyRuleItem rule, String targetServiceName) { return routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) .getNextInvokeVersion(rule); } public TagItem getFallbackFilteredServerTagItem(PolicyRuleItem rule, String targetServiceName) { return routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) .getFallbackNextInvokeVersion(rule); } /** * 1.filter set route rules targetService, build fallback targetService map and unSetTagInstances list. * 2.establish map is a more complicate way than direct traversal, because of multiple matches. * * the method getProperties() contains other field that we don't need. */ private Map> getDistributList(String serviceName, List list, PolicyRuleItem invokeRule, List unSetTagInstances, Map> fallbackVersionMap) { Map> versionServerMap = new HashMap<>(); for (INSTANCE instance : list) { //get server if (getServerName.apply(instance).equals(serviceName)) { TagItem tagItem = new TagItem(getVersion.apply(instance), getProperties.apply(instance)); // route most matching TagItem TagItem targetTag = buildTargetTag(invokeRule.getRoute(), tagItem); if (targetTag != null) { if (!versionServerMap.containsKey(targetTag)) { versionServerMap.put(targetTag, new ArrayList<>()); } versionServerMap.get(targetTag).add(instance); } else { // not matched, placed in the unset tag instances collection unSetTagInstances.add(instance); } // ensure the tags can build when set for both route and fallback at the same time if (!CollectionUtils.isEmpty(invokeRule.getFallback())) { // fallback most matching TagItem TagItem targetTagFallback = buildTargetTag(invokeRule.getFallback(), tagItem); if (!fallbackVersionMap.containsKey(targetTagFallback)) { fallbackVersionMap.put(targetTagFallback, new ArrayList<>()); } fallbackVersionMap.get(targetTagFallback).add(instance); } } } return versionServerMap; } private TagItem buildTargetTag(List route, TagItem tagItem) { int maxMatch = 0; TagItem targetTag = null; // obtain the rule with the most parameter matches for (RouteItem entry : route) { if (entry.getTagitem() == null){ continue; } int nowMatch = entry.getTagitem().matchNum(tagItem); if (nowMatch > maxMatch) { maxMatch = nowMatch; targetTag = entry.getTagitem(); } } return targetTag; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.distribute; import java.util.List; import java.util.Map; import java.util.function.Function; import org.apache.servicecomb.router.model.PolicyRuleItem; /** * @author GuoYl123 * @since 2019/10/17 **/ public interface RouterDistributor { void init(Function getVersion, Function getServerName, Function> getProperties); List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule); } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.exception; /** * @author GuoYl123 * @since 2019/11/4 **/ public class RouterIllegalParamException extends RuntimeException { private static final long serialVersionUID = 4359709211352400087L; public RouterIllegalParamException(String message) { super(message); } public RouterIllegalParamException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.match; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.model.PolicyRuleItem; public class RouterRuleMatcher { private final RouterRuleCache routerRuleCache; private final RequestProcessor requestProcessor; public RouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { this.routerRuleCache = routerRuleCache; this.requestProcessor = requestProcessor; } public PolicyRuleItem match(String serviceName, GovernanceRequestExtractor request) { for (PolicyRuleItem rule : routerRuleCache.getServiceInfoCacheMap().get(serviceName) .getAllrule()) { if (rule.getMatch() == null || requestProcessor.match(request, rule.getMatch())) { return rule; } } return null; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.model; import org.apache.servicecomb.router.exception.RouterIllegalParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author GuoYl123 * @since 2019/10/17 **/ public class HeaderRule { private static final Logger LOGGER = LoggerFactory.getLogger(HeaderRule.class); private String regex; private boolean caseInsensitive = true; private String exact; public HeaderRule() { } public boolean match(String str) { if (str == null) { return false; } if (exact == null && regex == null) { throw new RouterIllegalParamException( "route management regex and exact can not br null at same time."); } if (caseInsensitive) { str = str.toLowerCase(); exact = exact == null ? null : exact.toLowerCase(); regex = regex == null ? null : regex.toLowerCase(); } if (exact != null && !str.equals(exact)) { return false; } try { if (regex != null && !str.matches(regex)) { return false; } } catch (Exception e) { LOGGER.error("route management wrong regular expression format: {}", regex); return false; } return true; } public String getRegex() { return regex; } public void setRegex(String regex) { this.regex = regex; } public boolean isCaseInsensitive() { return caseInsensitive; } public void setCaseInsensitive(boolean caseInsensitive) { this.caseInsensitive = caseInsensitive; } public String getExact() { return exact; } public void setExact(String exact) { this.exact = exact; } @Override public String toString() { return "HeaderRule{" + "regex='" + regex + '\'' + ", caseInsensitive=" + caseInsensitive + ", exact='" + exact + '\'' + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.model; import java.util.List; import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.router.exception.RouterIllegalParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; /** * @author GuoYl123 * @since 2019/10/17 **/ public class PolicyRuleItem implements Comparable { private static final Logger LOGGER = LoggerFactory.getLogger(PolicyRuleItem.class); private Integer precedence; private Matcher match; private List route; private Integer total; private boolean weightLess = false; private List fallback; private Integer fallbackTotal; private boolean emptyProtection = true; public PolicyRuleItem() { } /** * if weight is less than 100, fill with minimum version * */ public void check() { if (CollectionUtils.isEmpty(route)) { throw new RouterIllegalParamException("canary rule list can not be null"); } int sum = 0; for (RouteItem item : route) { if (item.getWeight() == null) { throw new RouterIllegalParamException("canary rule weight can not be null"); } sum += item.getWeight(); } if (sum > 100) { LOGGER.warn("canary rule weight sum is more than 100"); } else if (sum < 100) { weightLess = true; route.add(new RouteItem(100 - sum, null)); } } @Override public int compareTo(PolicyRuleItem param) { return Integer.compare(param.precedence, this.precedence); } public Integer getPrecedence() { return precedence; } public void setPrecedence(Integer precedence) { this.precedence = precedence; } public Matcher getMatch() { return match; } public void setMatch(Matcher match) { this.match = match; } public List getRoute() { return route; } public void setRoute(List route) { this.route = route; } public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } public boolean isWeightLess() { return weightLess; } public void setWeightLess(boolean weightLess) { this.weightLess = weightLess; } public List getFallback() { return fallback; } public void setFallback(List fallback) { this.fallback = fallback; } public Integer getFallbackTotal() { return fallbackTotal; } public void setFallbackTotal(Integer fallbackTotal) { this.fallbackTotal = fallbackTotal; } public boolean isEmptyProtection() { return emptyProtection; } public void setEmptyProtection(boolean emptyProtection) { this.emptyProtection = emptyProtection; } @Override public String toString() { return "PolicyRuleItem{" + "precedence=" + precedence + ", match=" + match + ", route=" + route + ", total=" + total + ", weightLess=" + weightLess + ", fallback=" + fallback + ", fallbackTotal=" + fallbackTotal + ", emptyProtection=" + emptyProtection + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.model; import java.util.Map; /** * @author GuoYl123 * @since 2019/10/17 **/ public class RouteItem implements Comparable { private Integer weight; /** * for load balance */ private Integer currentWeight = 0; private Map tags; private TagItem tagitem; public void initTagItem() { if (tags != null) { tagitem = new TagItem(tags); } } public void addCurrentWeight() { currentWeight += weight; } public void reduceCurrentWeight(int total) { currentWeight -= total; } public RouteItem() { } public RouteItem(Integer weight, TagItem tags) { this.weight = weight; this.tagitem = tags; } public Integer getWeight() { return weight; } public void setWeight(Integer weight) { this.weight = weight; } public Integer getCurrentWeight() { return currentWeight; } public void setCurrentWeight(Integer currentWeight) { this.currentWeight = currentWeight; } public Map getTags() { return tags; } public void setTags(Map tags) { this.tags = tags; } public TagItem getTagitem() { return tagitem; } public void setTagitem(TagItem tagitem) { this.tagitem = tagitem; } @Override public int compareTo(RouteItem param) { return Integer.compare(param.weight, this.weight); } @Override public String toString() { return "RouteItem{" + "weight=" + weight + ", currentWeight=" + currentWeight + ", tags=" + tags + ", tagitem=" + tagitem + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.model; import java.util.List; import java.util.stream.Collectors; import org.springframework.util.CollectionUtils; /** * @author GuoYl123 * @since 2019/10/17 **/ public class ServiceInfoCache { private final List allrule; /** * for default version */ private TagItem latestVersionTag; public ServiceInfoCache(List policyRuleItemList) { this.allrule = policyRuleItemList.stream().sorted().collect(Collectors.toList()); this.getAllrule().forEach(rule -> { rule.getRoute().forEach(RouteItem::initTagItem); if (!CollectionUtils.isEmpty(rule.getFallback())) { rule.getFallback().forEach(RouteItem::initTagItem); } }); } public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { List rule = policyRuleItem.getRoute(); if (policyRuleItem.getTotal() == null) { policyRuleItem.setTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); } return calculateWeight(rule, policyRuleItem.getTotal()); } public TagItem getFallbackNextInvokeVersion(PolicyRuleItem policyRuleItem) { List rule = policyRuleItem.getFallback(); if (policyRuleItem.getFallbackTotal() == null) { policyRuleItem.setFallbackTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); } return calculateWeight(rule, policyRuleItem.getFallbackTotal()); } private TagItem calculateWeight(List rule, int total) { rule.forEach(RouteItem::addCurrentWeight); int maxIndex = 0, maxWeight = -1; for (int i = 0; i < rule.size(); i++) { if (maxWeight < rule.get(i).getCurrentWeight()) { maxIndex = i; maxWeight = rule.get(i).getCurrentWeight(); } } rule.get(maxIndex).reduceCurrentWeight(total); return rule.get(maxIndex).getTagitem(); } public List getAllrule() { return allrule; } public TagItem getLatestVersionTag() { return latestVersionTag; } public void setLatestVersionTag(TagItem latestVersionTag) { this.latestVersionTag = latestVersionTag; } @Override public String toString() { return "ServiceInfoCache{" + "allrule=" + allrule + ", latestVersionTag=" + latestVersionTag + '}'; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.model; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * @author GuoYl123 * @since 2019/10/17 **/ public class TagItem { private static final String VERSION = "version"; private String version; private Map param; public TagItem() { } public TagItem(String version, Map param) { this.version = version; this.param = param; } public TagItem(String version) { this.version = version; Map param = new HashMap<>(); param.put(VERSION, version); this.param = param; } public TagItem(Map param) { if (param.containsKey(VERSION)) { this.version = param.get(VERSION); } this.param = param; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Map getParam() { return param; } public void setParam(Map param) { this.param = param; } @Override public int hashCode() { return Objects.hash(getVersion(), getParam()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TagItem)) { return false; } TagItem tagItem = (TagItem) o; return Objects.equals(getVersion(), tagItem.getVersion()) && Objects.equals(getParam(), tagItem.getParam()); } /** * return match num * * @param item * @return */ public int matchNum(TagItem item) { int cnt = 0; if (version != null && !version.equals(item.version)) { return 0; } for (Map.Entry entry : param.entrySet()) { if (item.getParam().containsKey(entry.getKey()) && !item.getParam().get(entry.getKey()).equals(entry.getValue())) { return 0; } cnt++; } return cnt; } } ================================================ FILE: governance/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.util; import org.apache.servicecomb.foundation.common.Version; public class VersionCompareUtil { public static int compareVersion(String version1, String version2) { return new Version(version1).compareTo(new Version(version2)); } } ================================================ FILE: governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.governance.GovernanceCommonConfiguration org.apache.servicecomb.router.RouterCommonConfiguration ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractFailurePredictor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class AbstractFailurePredictorTest { class MyAbstractFailurePredictor extends AbstractFailurePredictor { MyAbstractFailurePredictor() { } @Override protected String extractStatusCode(Object result) { return (String) result; } @Override public boolean isFailedResult(List statusList, Throwable e) { return super.isFailedResult(statusList, e); } } @Test public void testCodeMatch() { AbstractFailurePredictor predictor = new MyAbstractFailurePredictor(); List statusList = Arrays.asList("500"); Assertions.assertTrue(predictor.isFailedResult(statusList, "500")); Assertions.assertFalse(predictor.isFailedResult(statusList, "502")); Assertions.assertFalse(predictor.isFailedResult(statusList, "400")); Assertions.assertFalse(predictor.isFailedResult(statusList, "444")); statusList = Arrays.asList("5x0"); Assertions.assertTrue(predictor.isFailedResult(statusList, "500")); Assertions.assertFalse(predictor.isFailedResult(statusList, "502")); Assertions.assertFalse(predictor.isFailedResult(statusList, "400")); Assertions.assertFalse(predictor.isFailedResult(statusList, "444")); statusList = Arrays.asList(null, "xx", "5x0"); Assertions.assertTrue(predictor.isFailedResult(statusList, "500")); Assertions.assertFalse(predictor.isFailedResult(statusList, "502")); Assertions.assertFalse(predictor.isFailedResult(statusList, "400")); Assertions.assertFalse(predictor.isFailedResult(statusList, "444")); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.policy.BulkheadPolicy; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class BulkheadHandlerTest { private BulkheadHandler bulkheadHandler; @Autowired public void setInstanceIsolationHandler(BulkheadHandler scbBulkheadHandler) { this.bulkheadHandler = scbBulkheadHandler; } @Test public void testMatchPriorityPolicy() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead"); BulkheadPolicy policy = bulkheadHandler.matchPolicy(request); Assertions.assertEquals("demo-bulkhead-priority", policy.getName()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.marker.CustomMatcher; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.governance.mockclasses.service.MockConfigurationForCustomMatcher; import org.junit.Assert; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import org.junit.jupiter.api.Assertions; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class, MockConfigurationForCustomMatcher.class}) public class CustomMatchTest { private RequestProcessor requestProcessor; @Autowired public void setRequestProcessor(RequestProcessor requestProcessor) { this.requestProcessor = requestProcessor; } private Matcher generateMatcher(String customMatcherHandler, String customMatcherParameters) { CustomMatcher customMatcher = new CustomMatcher(); customMatcher.setCustomMatcherHandler(customMatcherHandler); customMatcher.setCustomMatcherParameters(customMatcherParameters); Matcher matcher = new Matcher(); matcher.setCustomMatcher(customMatcher); return matcher; } @Test public void test_should_pass_when_value_class_empty() { GovernanceRequest request = new GovernanceRequest(); Matcher mockMatcher = generateMatcher("", ""); Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); mockMatcher = generateMatcher("", "bill"); Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); mockMatcher = generateMatcher("classWithAnnotation", ""); Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); } @Test public void test_should_pass_when_value_class_match() { GovernanceRequest request = new GovernanceRequest(); Matcher mockMatcher = generateMatcher("classWithAnnotation", "bill"); Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); } @Test public void test_should_throw_exception_when_class_not_found() { GovernanceRequest request = new GovernanceRequest(); try { Matcher mockMatcher = generateMatcher("classWithAnnotationNotFound", "bill"); this.requestProcessor.match(request, mockMatcher); Assertions.fail("an exception is expected!"); } catch (Exception e) { Assert.assertTrue(e.getCause() instanceof ClassNotFoundException); } } @Test public void test_should_pass_when_multiple_value() { GovernanceRequest request = new GovernanceRequest(); Matcher mockMatcher = generateMatcher("classWithAnnotation", "bill,bill2"); Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); } @Test public void test_should_throw_exception_when_not_implements_interface() { GovernanceRequest request = new GovernanceRequest(); try { Matcher mockMatcher = generateMatcher("classNotImplements", "bill,bill2"); this.requestProcessor.match(request, mockMatcher); Assertions.fail("an exception is expected!"); } catch (Exception e) { Assert.assertTrue(e.getMessage().contains(RequestProcessor.errorMessageForNotImplements)); } } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.governance.handler.FaultInjectionHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.processor.injection.Fault; import org.apache.servicecomb.governance.processor.injection.FaultInjectionDecorators; import org.apache.servicecomb.governance.processor.injection.FaultInjectionDecorators.FaultInjectionDecorateCheckedSupplier; import org.apache.servicecomb.governance.processor.injection.FaultInjectionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class FaultInjectionTest { private FaultInjectionHandler faultInjectionHandler; private FaultInjectionHandler faultInjectionHandler2; @Autowired public void setFaultInjectionHandler(FaultInjectionHandler scbFaultInjectionHandler, @Qualifier("faultInjectionHandler2") FaultInjectionHandler faultInjectionHandler2) { this.faultInjectionHandler = scbFaultInjectionHandler; this.faultInjectionHandler2 = faultInjectionHandler2; } public FaultInjectionTest() { } @Test public void test_delay_fault_injection_service_name_work() throws Throwable { FaultInjectionDecorateCheckedSupplier ds = FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/faultInjectDelay"); request.setServiceName("srcService"); Fault fault = faultInjectionHandler.getActuator(request); ds.withFaultInjection(fault); Assertions.assertEquals("test", ds.get()); // flow control CountDownLatch cd = new CountDownLatch(10); AtomicBoolean expected = new AtomicBoolean(false); AtomicBoolean notExpected = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { new Thread(() -> { try { long startTime = System.currentTimeMillis(); Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } // delayTime is 2S if (System.currentTimeMillis() - startTime > 1000) { expected.set(true); } } catch (Throwable e) { notExpected.set(true); } cd.countDown(); }).start(); } //timeout should be bigger than delayTime cd.await(10, TimeUnit.SECONDS); Assertions.assertFalse(notExpected.get()); Assertions.assertTrue(expected.get()); } @Test public void test_abort_fault_injection_service_name_work() throws Throwable { FaultInjectionDecorateCheckedSupplier ds = FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/faultInjectAbort"); request.setServiceName("srcService"); Fault fault = faultInjectionHandler.getActuator(request); ds.withFaultInjection(fault); Assertions.assertEquals("test", ds.get()); // flow control CountDownLatch cd = new CountDownLatch(10); AtomicBoolean expected = new AtomicBoolean(false); AtomicBoolean notExpected = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { new Thread(() -> { try { Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } } catch (FaultInjectionException e) { expected.set(true); } catch (Throwable e) { notExpected.set(true); } cd.countDown(); }).start(); } cd.await(1, TimeUnit.SECONDS); Assertions.assertFalse(notExpected.get()); Assertions.assertTrue(expected.get()); } @Test public void test_fallback_returnNull_work() throws Throwable { FaultInjectionDecorateCheckedSupplier ds = FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/returnNull"); request.setServiceName("returnNull"); Fault fault = faultInjectionHandler.getActuator(request); ds.withFaultInjection(fault); Assertions.assertEquals(null, ds.get()); } @Test public void test_fallback_ThrowException_work() throws Throwable { FaultInjectionDecorateCheckedSupplier ds = FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/throwException"); request.setServiceName("ThrowException"); Fault fault = faultInjectionHandler.getActuator(request); ds.withFaultInjection(fault); boolean expected = false; try { ds.get(); } catch (FaultInjectionException e) { if (e.getFaultResponse().getErrorCode() == 500) { expected = true; } } Assertions.assertEquals(true, expected); } @Test public void test_fallback_forceClosed_work() throws Throwable { FaultInjectionDecorateCheckedSupplier ds = FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/forceClosed"); request.setServiceName("forceClosed"); Fault fault = faultInjectionHandler.getActuator(request); ds.withFaultInjection(fault); Assertions.assertEquals("test", ds.get()); } @Test public void test_fallback_ThrowException_work_handler2() throws Throwable { FaultInjectionDecorateCheckedSupplier ds = FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/throwException"); request.setServiceName("ThrowException"); Fault fault = faultInjectionHandler2.getActuator(request); ds.withFaultInjection(fault); boolean expected = false; try { ds.get(); } catch (FaultInjectionException e) { if (e.getFaultResponse().getErrorCode() == 500) { expected = true; } } Assertions.assertEquals(true, expected); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RequestNotPermitted; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class FlowControlTest { private RateLimitingHandler rateLimitingHandler; @Autowired public void setRateLimitingHandler(RateLimitingHandler rateLimitingHandler) { this.rateLimitingHandler = rateLimitingHandler; } public FlowControlTest() { } @Test public void test_rate_limiting_work() throws Throwable { DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/hello"); RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); ds.withRateLimiter(rateLimiter); Assertions.assertEquals("test", ds.get()); // flow control CountDownLatch cd = new CountDownLatch(10); AtomicBoolean expected = new AtomicBoolean(false); AtomicBoolean notExpected = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { new Thread(() -> { try { Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } } catch (Throwable e) { if (e instanceof RequestNotPermitted) { expected.set(true); } else { notExpected.set(true); } } cd.countDown(); }).start(); } cd.await(1, TimeUnit.SECONDS); Assertions.assertTrue(expected.get()); Assertions.assertFalse(notExpected.get()); } @Test public void test_rate_limiting_service_name_work() throws Throwable { DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/helloServiceName"); request.setServiceName("srcService"); RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); ds.withRateLimiter(rateLimiter); Assertions.assertEquals("test", ds.get()); // flow control CountDownLatch cd = new CountDownLatch(10); AtomicBoolean expected = new AtomicBoolean(false); AtomicBoolean notExpected = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { new Thread(() -> { try { Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } } catch (Throwable e) { if (e instanceof RequestNotPermitted) { expected.set(true); } else { notExpected.set(true); } } cd.countDown(); }).start(); } cd.await(1, TimeUnit.SECONDS); Assertions.assertTrue(expected.get()); Assertions.assertFalse(notExpected.get()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/GovernanceCacheHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.GovernanceCacheHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; import org.apache.servicecomb.governance.service.GovernanceCache; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class GovernanceCacheHandlerTest { private GovernanceCacheHandler governanceCacheHandler; @Autowired public void setInstanceIsolationHandler(@Autowired GovernanceCacheHandler governanceCacheHandler) { this.governanceCacheHandler = governanceCacheHandler; } @Test public void testMatchPriorityPolicy() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/governanceCache"); GovernanceCachePolicy policy = governanceCacheHandler.matchPolicy(request); Assertions.assertEquals("demo-governanceCache", policy.getName()); GovernanceCache governanceCache = governanceCacheHandler.getActuator(request); governanceCache.putValueIntoCache("governance", "Cache"); Object cache = governanceCache.getValueFromCache("governance"); Assertions.assertEquals("Cache", cache); governanceCache.putValueIntoCache("response", null); Object response = governanceCache.getValueFromCache("response"); Assertions.assertNull(response); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.time.Duration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.governance.marker.TrafficMarker; import org.apache.servicecomb.governance.policy.AbstractPolicy; import org.apache.servicecomb.governance.policy.BulkheadPolicy; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; import org.apache.servicecomb.governance.policy.RateLimitingPolicy; import org.apache.servicecomb.governance.policy.RetryPolicy; import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; import org.apache.servicecomb.governance.processor.injection.FaultInjectionConst; import org.apache.servicecomb.governance.properties.BulkheadProperties; import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; import org.apache.servicecomb.governance.properties.FaultInjectionProperties; import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.properties.GovernanceProperties; import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; import org.apache.servicecomb.governance.properties.MatchProperties; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.apache.servicecomb.governance.properties.RetryProperties; import org.apache.servicecomb.governance.properties.TimeLimiterProperties; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class GovernancePropertiesTest { private List> propertiesList; private MatchProperties matchProperties; private BulkheadProperties bulkheadProperties; private CircuitBreakerProperties circuitBreakerProperties; private TimeLimiterProperties timeLimiterProperties; private GovernanceCacheProperties governanceCacheProperties; private InstanceIsolationProperties instanceIsolationProperties; private RateLimitProperties rateLimitProperties; private RetryProperties retryProperties; private FaultInjectionProperties faultInjectionProperties; private Environment environment; @Autowired public void setPropertiesList(List> propertiesList) { this.propertiesList = propertiesList; } @Autowired public void setMatchProperties(MatchProperties scbMatchProperties) { this.matchProperties = scbMatchProperties; } @Autowired public void setBulkheadProperties(BulkheadProperties scbBulkheadProperties) { this.bulkheadProperties = scbBulkheadProperties; } @Autowired public void setCircuitBreakerProperties(CircuitBreakerProperties scbCircuitBreakerProperties) { this.circuitBreakerProperties = scbCircuitBreakerProperties; } @Autowired public void setTimeLimiterProperties(TimeLimiterProperties scbTimeLimiterProperties) { this.timeLimiterProperties = scbTimeLimiterProperties; } @Autowired public void setGovernanceCacheProperties(GovernanceCacheProperties scbGovernanceCacheProperties) { this.governanceCacheProperties = scbGovernanceCacheProperties; } @Autowired public void setRateLimitProperties(RateLimitProperties scbRateLimitProperties) { this.rateLimitProperties = scbRateLimitProperties; } @Autowired public void setInstanceIsolationProperties(InstanceIsolationProperties scbInstanceIsolationProperties) { this.instanceIsolationProperties = scbInstanceIsolationProperties; } @Autowired public void setRetryProperties(RetryProperties scbRetryProperties) { this.retryProperties = scbRetryProperties; } @Autowired public void setFaultInjectionProperties( FaultInjectionProperties scbFaultInjectionProperties) { this.faultInjectionProperties = scbFaultInjectionProperties; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setDynamicValues(Map dynamicValues) { this.dynamicValues = dynamicValues; } public GovernancePropertiesTest() { } private Map dynamicValues = new HashMap<>(); private static final float DELTA = 0.0000001f; @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; if (configurableEnvironment.getPropertySources().contains("testDynamicChange")) { configurableEnvironment.getPropertySources().remove("testDynamicChange"); } configurableEnvironment.getPropertySources() .addFirst(new EnumerablePropertySource>("testDynamicChange", dynamicValues) { @Override public Object getProperty(String s) { return this.getSource().get(s); } @Override public String[] getPropertyNames() { return this.getSource().keySet().toArray(new String[0]); } }); } @AfterEach public void tearDown() { Set keys = dynamicValues.keySet(); keys.forEach(k -> dynamicValues.put(k, null)); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); } @Test public void test_all_bean_is_loaded() { Assertions.assertEquals(16, propertiesList.size()); } @Test public void test_match_properties_successfully_loaded() { Map markers = matchProperties.getParsedEntity(); TrafficMarker demoRateLimiting = markers.get("demo-rateLimiting"); List matchers = demoRateLimiting.getMatches(); Assertions.assertEquals(1, matchers.size()); Matcher matcher = matchers.get(0); Assertions.assertEquals("/hello", matcher.getApiPath().get("exact")); TrafficMarker demoBulkhead = markers.get("demo-bulkhead"); matchers = demoBulkhead.getMatches(); Assertions.assertEquals(2, matchers.size()); matcher = matchers.get(0); Assertions.assertEquals("/bulkhead", matcher.getApiPath().get("exact")); Assertions.assertEquals("matchPath", matcher.getName()); } @Test public void test_match_properties_delete() { Map markers = matchProperties.getParsedEntity(); Assertions.assertEquals(null, markers.get("test")); dynamicValues.put("servicecomb.matchGroup.test", "matches:\n" + " - apiPath:\n" + " exact: \"/hello2\"\n" + " name: match0"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); markers = matchProperties.getParsedEntity(); Assertions.assertEquals(1, markers.get("test").getMatches().size()); tearDown(); markers = matchProperties.getParsedEntity(); Assertions.assertEquals(null, markers.get("test")); } @Test public void test_match_properties_changed() { dynamicValues.put("servicecomb.matchGroup.demo-rateLimiting", "matches:\n" + " - apiPath:\n" + " exact: \"/hello2\"\n" + " name: match0"); dynamicValues.put("servicecomb.matchGroup.demo-rateLimiting2", "matches:\n" + " - apiPath:\n" + " exact: \"/hello2\"\n" + " name: match0"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map markers = matchProperties.getParsedEntity(); TrafficMarker demoRateLimiting = markers.get("demo-rateLimiting"); List matchers = demoRateLimiting.getMatches(); Assertions.assertEquals(1, matchers.size()); Matcher matcher = matchers.get(0); Assertions.assertEquals("/hello2", matcher.getApiPath().get("exact")); demoRateLimiting = markers.get("demo-rateLimiting2"); matchers = demoRateLimiting.getMatches(); Assertions.assertEquals(1, matchers.size()); matcher = matchers.get(0); Assertions.assertEquals("/hello2", matcher.getApiPath().get("exact")); } @Test public void test_bulkhead_properties_changed() { dynamicValues.put("servicecomb.bulkhead.demo-bulkhead", "rules:\n" + "maxConcurrentCalls: 2\n" + "maxWaitDuration: 2000"); dynamicValues.put("servicecomb.bulkhead.bulkhead1", "rules:\n" + "maxConcurrentCalls: 3\n" + "maxWaitDuration: 3000"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = bulkheadProperties.getParsedEntity(); Assertions.assertEquals(3, policies.size()); BulkheadPolicy policy = policies.get("demo-bulkhead"); Assertions.assertEquals(2, policy.getMaxConcurrentCalls()); Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); policies = bulkheadProperties.getParsedEntity(); Assertions.assertEquals(3, policies.size()); policy = policies.get("bulkhead1"); Assertions.assertEquals(3, policy.getMaxConcurrentCalls()); Assertions.assertEquals(3000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); Assertions.assertEquals("bulkhead1", policy.getName()); } @Test public void test_bulkhead_properties_bound() { dynamicValues.put("servicecomb.bulkhead.test-bulkhead1", "rules:\n" + "maxConcurrentCalls: 0\n" + "maxWaitDuration: 2000"); dynamicValues.put("servicecomb.bulkhead.test-bulkhead2", "rules:\n" + "maxConcurrentCalls: 1000\n" + "maxWaitDuration: 0"); dynamicValues.put("servicecomb.bulkhead.test-bulkhead3", "rules:\n" + "maxConcurrentCalls: 0\n" + "maxWaitDuration: 2S"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = bulkheadProperties.getParsedEntity(); Assertions.assertEquals(5, policies.size()); BulkheadPolicy policy = policies.get("test-bulkhead1"); Assertions.assertEquals(0, policy.getMaxConcurrentCalls()); Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); policy = policies.get("test-bulkhead2"); Assertions.assertEquals(1000, policy.getMaxConcurrentCalls()); Assertions.assertEquals(0, Duration.parse(policy.getMaxWaitDuration()).toMillis()); Assertions.assertEquals("test-bulkhead2", policy.getName()); policy = policies.get("test-bulkhead3"); Assertions.assertEquals(0, policy.getMaxConcurrentCalls()); Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); Assertions.assertEquals("test-bulkhead3", policy.getName()); } @Test public void test_bulkhead_properties_successfully_loaded() { Map policies = bulkheadProperties.getParsedEntity(); Assertions.assertEquals(2, policies.size()); BulkheadPolicy policy = policies.get("demo-bulkhead"); Assertions.assertEquals(1, policy.getMaxConcurrentCalls()); Assertions.assertEquals(3000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); } @Test public void test_timelimiter_properties_successfully_loaded() { Map policies = timeLimiterProperties.getParsedEntity(); Assertions.assertEquals(2, policies.size()); TimeLimiterPolicy timeLimiterPolicy = policies.get("demo-timeLimiter-other"); Assertions.assertEquals(2000, Duration.parse(timeLimiterPolicy.getTimeoutDuration()).toMillis()); Assertions.assertEquals(false, timeLimiterPolicy.isCancelRunningFuture()); } @Test public void test_timelimiter_properties_bound() { dynamicValues.put("servicecomb.timeLimiter.name1", "rules:\n" + "timeoutDuration: 5000\n" + "cancelRunningFuture: false"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = timeLimiterProperties.getParsedEntity(); Assertions.assertEquals(3, policies.size()); TimeLimiterPolicy policy = policies.get("name1"); Assertions.assertEquals(false, policy.isCancelRunningFuture()); Assertions.assertEquals(5000, Duration.parse(policy.getTimeoutDuration()).toMillis()); } @Test public void test_governanceCache_properties_successfully_loaded() { Map policies = governanceCacheProperties.getParsedEntity(); Assertions.assertEquals(2, policies.size()); GovernanceCachePolicy governanceCachePolicy = policies.get("demo-governanceCache-other"); Assertions.assertEquals(15, governanceCachePolicy.getConcurrencyLevel()); Assertions.assertEquals(50000, governanceCachePolicy.getMaximumSize()); Assertions.assertEquals(666666, Duration.parse(governanceCachePolicy.getTtl()).toMillis()); } @Test public void test_governanceCache_properties_bound() { dynamicValues.put("servicecomb.cache.name1", "rules:\n" + "ttl: 3000000\n" + "maximumSize: 2000\n" + "concurrencyLevel: 6"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = governanceCacheProperties.getParsedEntity(); Assertions.assertEquals(3, policies.size()); GovernanceCachePolicy policy = policies.get("name1"); Assertions.assertEquals(3000000, Duration.parse(policy.getTtl()).toMillis()); Assertions.assertEquals(2000, policy.getMaximumSize()); Assertions.assertEquals(6, policy.getConcurrencyLevel()); } @Test public void test_circuit_breaker_properties_successfully_loaded() { Map policies = circuitBreakerProperties.getParsedEntity(); Assertions.assertEquals(1, policies.size()); CircuitBreakerPolicy policy = policies.get("demo-circuitBreaker"); Assertions.assertEquals(2, policy.getMinimumNumberOfCalls()); Assertions.assertEquals("2", policy.getSlidingWindowSize()); } @Test public void test_circuit_breaker_properties_Of_windows_size() { dynamicValues.put("servicecomb.circuitBreaker.name1", "rules:\n" + "slidingWindowType: count\n" + "slidingWindowSize: 2"); dynamicValues.put("servicecomb.circuitBreaker.name2", "rules:\n" + "slidingWindowType: time\n" + "slidingWindowSize: 2"); dynamicValues.put("servicecomb.circuitBreaker.name3", "rules:\n" + "slidingWindowType: test\n" + "slidingWindowSize: 1M"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = circuitBreakerProperties.getParsedEntity(); Assertions.assertEquals(4, policies.size()); CircuitBreakerPolicy policy = policies.get("name1"); Assertions.assertEquals("count", policy.getSlidingWindowType()); Assertions.assertEquals("2", policy.getSlidingWindowSize()); policy = policies.get("name2"); Assertions.assertEquals("time", policy.getSlidingWindowType()); Assertions.assertEquals("2", policy.getSlidingWindowSize()); policy = policies.get("name3"); Assertions.assertEquals("60", policy.getSlidingWindowSize()); } @Test public void test_circuit_breaker_properties_Of_type() { dynamicValues.put("servicecomb.circuitBreaker.type1", "rules:\n" + "failureRateThreshold: 20\n" + "slowCallRateThreshold: 100"); dynamicValues.put("servicecomb.circuitBreaker.type2", "rules:\n" + "failureRateThreshold: 20.33\n" + "slowCallRateThreshold: 0.01"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = circuitBreakerProperties.getParsedEntity(); CircuitBreakerPolicy policy = policies.get("type1"); Assertions.assertEquals(20.0f, policy.getFailureRateThreshold(), DELTA); Assertions.assertEquals(100.0f, policy.getSlowCallRateThreshold(), DELTA); policy = policies.get("type2"); Assertions.assertEquals(20.33f, policy.getFailureRateThreshold(), DELTA); Assertions.assertEquals(0.01f, policy.getSlowCallRateThreshold(), DELTA); } @Test public void test_rate_limit_properties_successfully_loaded() { Map policies = rateLimitProperties.getParsedEntity(); Assertions.assertEquals(2, policies.size()); RateLimitingPolicy policy = policies.get("demo-rateLimiting"); Assertions.assertEquals(1, policy.getRate()); } @Test public void test_retry_properties_successfully_loaded() { Map policies = retryProperties.getParsedEntity(); Assertions.assertEquals(1, policies.size()); RetryPolicy policy = policies.get("demo-retry"); Assertions.assertEquals(3, policy.getMaxAttempts()); } @Test public void test_properties_changed_to_duration() { dynamicValues.put("servicecomb.bulkhead.test1", "rules:\n" + "maxConcurrentCalls: 2\n" + "maxWaitDuration: 2S"); dynamicValues.put("servicecomb.bulkhead.test2", "rules:\n" + "maxConcurrentCalls: 3\n" + "maxWaitDuration: 1M"); GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); Map policies = bulkheadProperties.getParsedEntity(); Assertions.assertEquals(4, policies.size()); BulkheadPolicy policy = policies.get("test1"); Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); policies = bulkheadProperties.getParsedEntity(); Assertions.assertEquals(4, policies.size()); policy = policies.get("test2"); Assertions.assertEquals(60000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); } @Test public void test_instance_isolation_properties_successfully_loaded() { Map policies = instanceIsolationProperties.getParsedEntity(); Assertions.assertEquals(1, policies.size()); CircuitBreakerPolicy policy = policies.get("demo-allOperation"); Assertions.assertEquals(2, policy.getMinimumNumberOfCalls()); Assertions.assertEquals("2", policy.getSlidingWindowSize()); } @Test public void test_fault_injection_properties_successfully_loaded() { Map policies = faultInjectionProperties.getParsedEntity(); Assertions.assertEquals(5, policies.size()); FaultInjectionPolicy policy = policies.get("demo-faultInjectDelay"); Assertions.assertEquals(FaultInjectionConst.TYPE_DELAY, policy.getType()); Assertions.assertEquals(2000, policy.getDelayTimeToMillis()); Assertions.assertEquals(100, policy.getPercentage()); policy = policies.get("demo-faultInjectAbort"); Assertions.assertEquals(FaultInjectionConst.TYPE_ABORT, policy.getType()); Assertions.assertEquals(50, policy.getPercentage()); Assertions.assertEquals(500, policy.getErrorCode()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/IdentifierRateLimitingHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.governance.handler.IdentifierRateLimitingHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RequestNotPermitted; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class IdentifierRateLimitingHandlerTest { private IdentifierRateLimitingHandler rateLimitingHandler; @Autowired public void setRateLimitingHandler(IdentifierRateLimitingHandler rateLimitingHandler) { this.rateLimitingHandler = rateLimitingHandler; } public IdentifierRateLimitingHandlerTest() { } @Test public void test_rate_limiting_work() throws Throwable { DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/hello"); Map headers = new HashMap<>(); headers.put("test", "1234"); request.setHeaders(headers); RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); ds.withRateLimiter(rateLimiter); Assertions.assertEquals("test", ds.get()); // flow control CountDownLatch cd = new CountDownLatch(10); AtomicBoolean expected = new AtomicBoolean(false); AtomicBoolean notExpected = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { new Thread(() -> { try { Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } } catch (Throwable e) { if (e instanceof RequestNotPermitted) { expected.set(true); } else { notExpected.set(true); } } cd.countDown(); }).start(); } cd.await(1, TimeUnit.SECONDS); Assertions.assertTrue(expected.get()); Assertions.assertFalse(notExpected.get()); } @Test public void test_rate_limiting_service_name_work() throws Throwable { DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/helloServiceName"); request.setServiceName("srcService"); Map headers = new HashMap<>(); headers.put("test", "1234"); request.setHeaders(headers); RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); ds.withRateLimiter(rateLimiter); Assertions.assertEquals("test", ds.get()); headers = new HashMap<>(); headers.put("test", "12345"); request.setHeaders(headers); RateLimiter rateLimiter2 = rateLimitingHandler.getActuator(request); DecorateCheckedSupplier ds2 = Decorators.ofCheckedSupplier(() -> "test2"); ds2.withRateLimiter(rateLimiter2); Assertions.assertEquals("test2", ds2.get()); // flow control CountDownLatch cd = new CountDownLatch(10); AtomicBoolean expected = new AtomicBoolean(false); AtomicBoolean notExpected = new AtomicBoolean(false); for (int i = 0; i < 10; i++) { new Thread(() -> { try { Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } } catch (Throwable e) { if (e instanceof RequestNotPermitted) { expected.set(true); } else { notExpected.set(true); } } cd.countDown(); }).start(); } cd.await(1, TimeUnit.SECONDS); Assertions.assertTrue(expected.get()); Assertions.assertFalse(notExpected.get()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/InstanceBulkheadHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadFullException; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class InstanceBulkheadHandlerTest { private InstanceBulkheadHandler instanceBulkheadHandler; @Autowired public void setInstanceBulkheadHandler(InstanceBulkheadHandler instanceBulkheadHandler) { this.instanceBulkheadHandler = instanceBulkheadHandler; } @Test public void test_instance_bulkhead_work() throws Throwable { // instance1 DecorateCheckedSupplier dsInstance1 = Decorators.ofCheckedSupplier(() -> "wake"); GovernanceRequest requestInstance1 = new GovernanceRequest(); requestInstance1.setInstanceId("instance01"); requestInstance1.setServiceName("service01"); requestInstance1.setApiPath("/test"); Bulkhead bulkheadInstance1 = instanceBulkheadHandler.getActuator(requestInstance1); dsInstance1.withBulkhead(bulkheadInstance1); // instance2 DecorateCheckedSupplier dsInstance2 = Decorators.ofCheckedSupplier(() -> { Thread.sleep(1000); return "sleep"; }); GovernanceRequest requestInstance2 = new GovernanceRequest(); requestInstance2.setInstanceId("instance02"); requestInstance2.setServiceName("service01"); requestInstance2.setApiPath("/test"); Bulkhead bulkheadInstance2 = instanceBulkheadHandler.getActuator(requestInstance2); dsInstance2.withBulkhead(bulkheadInstance2); Executor executor = Executors.newFixedThreadPool(4); AtomicInteger wakeCount = new AtomicInteger(0); AtomicInteger sleepCount = new AtomicInteger(0); AtomicInteger errorCount = new AtomicInteger(0); AtomicInteger rejectCount = new AtomicInteger(0); CountDownLatch countDownLatch = new CountDownLatch(100); for (int i = 0; i < 100; i++) { final int num = i; executor.execute(() -> { // 50% for each server if (num % 2 == 0) { runCommand(dsInstance1, wakeCount, sleepCount, errorCount, rejectCount, countDownLatch); } else { runCommand(dsInstance2, wakeCount, sleepCount, errorCount, rejectCount, countDownLatch); } }); } countDownLatch.await(100, TimeUnit.SECONDS); Assertions.assertEquals(50, wakeCount.get()); Assertions.assertEquals(2, sleepCount.get()); Assertions.assertEquals(0, errorCount.get()); Assertions.assertEquals(48, rejectCount.get()); } private void runCommand(DecorateCheckedSupplier ds, AtomicInteger wakeCount, AtomicInteger sleepCount, AtomicInteger errorCount, AtomicInteger rejectCount, CountDownLatch countDownLatch) { try { String result = ds.get(); if ("wake".equals(result)) { wakeCount.incrementAndGet(); } else if ("sleep".equals(result)) { sleepCount.incrementAndGet(); } else { errorCount.incrementAndGet(); } } catch (BulkheadFullException e) { rejectCount.incrementAndGet(); } catch (Throwable e) { errorCount.incrementAndGet(); } countDownLatch.countDown(); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/InstanceIsolationTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.net.ConnectException; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import io.github.resilience4j.circuitbreaker.CallNotPermittedException; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class InstanceIsolationTest { private InstanceIsolationHandler instanceIsolationHandler; private MeterRegistry meterRegistry; @Autowired public void setInstanceIsolationHandler(InstanceIsolationHandler instanceIsolationHandler, MeterRegistry meterRegistry) { this.instanceIsolationHandler = instanceIsolationHandler; this.meterRegistry = meterRegistry; } @Test public void test_instance_isolation_work() throws Throwable { AtomicInteger counter = new AtomicInteger(0); DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> { int run = counter.getAndIncrement(); if (run == 0) { return "test"; } if (run == 1) { throw new ConnectException("test exception"); } return "test"; }); DecorateCheckedSupplier ds2 = Decorators.ofCheckedSupplier(() -> "test"); GovernanceRequest request = new GovernanceRequest(); request.setInstanceId("instance01"); request.setServiceName("service01"); request.setApiPath("/test"); CircuitBreaker circuitBreaker = instanceIsolationHandler.getActuator(request); ds.withCircuitBreaker(circuitBreaker); // isolation from error Assertions.assertEquals("test", ds.get()); Assertions.assertThrows(ConnectException.class, ds::get); Assertions.assertThrows(CallNotPermittedException.class, ds::get); Assertions.assertThrows(CallNotPermittedException.class, ds::get); Assertions.assertThrows(CallNotPermittedException.class, ds::get); Assertions.assertThrows(CallNotPermittedException.class, ds::get); Assertions.assertThrows(CallNotPermittedException.class, ds::get); // isolation do not influence other instances GovernanceRequest request2 = new GovernanceRequest(); request2.setInstanceId("instance02"); request2.setServiceName("service01"); request2.setApiPath("/test"); CircuitBreaker circuitBreaker2 = instanceIsolationHandler.getActuator(request2); ds2.withCircuitBreaker(circuitBreaker2); Assertions.assertEquals("test", ds2.get()); Assertions.assertEquals("test", ds2.get()); Assertions.assertEquals("test", ds2.get()); Assertions.assertEquals("test", ds2.get()); assertMetricsNotFinish(); // recover from isolation Thread.sleep(1000); Assertions.assertEquals("test", ds.get()); Assertions.assertEquals("test", ds.get()); Assertions.assertEquals("test", ds2.get()); Assertions.assertEquals("test", ds2.get()); assertMetricsFinish(); } private void assertMetricsNotFinish() { String result = ((PrometheusMeterRegistry) meterRegistry).scrape(); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\",state=\"open\"} 1.0")); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\",state=\"closed\"} 1.0")); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\"} 1")); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\"} 4")); } private void assertMetricsFinish() { String result = ((PrometheusMeterRegistry) meterRegistry).scrape(); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\",state=\"closed\"} 1.0")); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\",state=\"closed\"} 1.0")); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\"} 3")); Assertions.assertTrue(result.contains( "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\"} 6")); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/LoadBalancerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.LoadBalanceHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.processor.loadbanlance.LoadBalance; import org.junit.Assert; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class LoadBalancerTest { private LoadBalanceHandler loadBalanceHandler; @Autowired public void setLoadBalanceHandler(LoadBalanceHandler loadBalanceHandler) { this.loadBalanceHandler = loadBalanceHandler; } public LoadBalancerTest() { } @Test public void test_loadbalance_random() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/loadrandom"); request.setServiceName("loadrandom"); LoadBalance loadBalance = loadBalanceHandler.getActuator(request); Assert.assertEquals("Random", loadBalance.getRule()); } @Test public void test_loadbalance_roundRobin() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/loadroundRobin"); request.setServiceName("loadroundRobin"); LoadBalance loadBalance = loadBalanceHandler.getActuator(request); Assert.assertEquals("RoundRobin", loadBalance.getRule()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/MapperTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.governance.handler.MapperHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.processor.mapping.Mapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class MapperTest { private MapperHandler mapperHandler; private MapperHandler mapperHandler2; @Autowired public void setMapperHandler(MapperHandler scbMapperHandler, @Qualifier("mapperHandler2") MapperHandler mapperHandler2) { this.mapperHandler = scbMapperHandler; this.mapperHandler2 = mapperHandler2; } @Test public void test_mapper_work() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/mapper/v1"); Mapper mapper = mapperHandler.getActuator(request); Assertions.assertEquals(2, mapper.target().size()); Assertions.assertEquals("127.0.0.1", mapper.target().get("host")); Assertions.assertEquals("8080", mapper.target().get("port")); } @Test public void test_mapper_query_work() { GovernanceRequest request = new GovernanceRequest(); Map queries = new HashMap<>(); queries.put("name", "bob"); request.setQueries(queries); Mapper mapper = mapperHandler.getActuator(request); Assertions.assertEquals(1, mapper.target().size()); Assertions.assertEquals("$Q{name}", mapper.target().get("user-id")); } @Test public void test_mapper2_work() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/mapper/v1"); Mapper mapper = mapperHandler2.getActuator(request); Assertions.assertEquals(2, mapper.target().size()); Assertions.assertEquals("127.0.0.1", mapper.target().get("host")); Assertions.assertEquals("9090", mapper.target().get("port")); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/MockCircuitBreakerExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; public class MockCircuitBreakerExtension extends AbstractCircuitBreakerExtension { @Override protected String extractStatusCode(Object result) { return "200"; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; import org.apache.servicecomb.governance.handler.FaultInjectionHandler; import org.apache.servicecomb.governance.handler.MapperHandler; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; import org.apache.servicecomb.governance.properties.BulkheadProperties; import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; import org.apache.servicecomb.governance.properties.FaultInjectionProperties; import org.apache.servicecomb.governance.properties.MapperProperties; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import io.micrometer.prometheusmetrics.PrometheusConfig; import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; @Configuration public class MockConfiguration { @Bean public MockMicroserviceMeta mockMicroserviceMeta() { return new MockMicroserviceMeta(); } @Bean public MockRetryExtension mockRetryExtension() { return new MockRetryExtension(); } @Bean public MockCircuitBreakerExtension circuitBreakerExtension() { return new MockCircuitBreakerExtension(); } @Bean public MockInstanceIsolationExtension instanceIsolationExtension() { return new MockInstanceIsolationExtension(); } @Bean public PrometheusMeterRegistry meterRegistry() { return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); } @Bean public MapperProperties mapperProperties2() { return new MapperProperties(MapperProperties.MATCH_MAPPER_KEY + "2"); } @Bean public MapperHandler mapperHandler2(@Qualifier("mapperProperties2") MapperProperties mapperProperties) { return new MapperHandler(mapperProperties); } @Bean public BulkheadProperties bulkheadProperties2() { return new BulkheadProperties(BulkheadProperties.MATCH_BULKHEAD_KEY + "2"); } @Bean public BulkheadHandler bulkheadHandler2(@Qualifier("bulkheadProperties2") BulkheadProperties bulkheadProperties) { return new BulkheadHandler(bulkheadProperties); } @Bean public CircuitBreakerProperties circuitBreakerProperties2() { return new CircuitBreakerProperties(CircuitBreakerProperties.MATCH_CIRCUITBREAKER_KEY + "2"); } @Bean public CircuitBreakerHandler circuitBreakerHandler2( @Qualifier("circuitBreakerProperties2") CircuitBreakerProperties circuitBreakerProperties, AbstractCircuitBreakerExtension circuitBreakerExtension) { return new CircuitBreakerHandler(circuitBreakerProperties, circuitBreakerExtension); } @Bean public FaultInjectionProperties faultInjectionProperties2() { return new FaultInjectionProperties(FaultInjectionProperties.MATCH_FAULT_INJECTION_KEY + "2"); } @Bean public FaultInjectionHandler faultInjectionHandler2( @Qualifier("faultInjectionProperties2") FaultInjectionProperties faultInjectionProperties) { return new FaultInjectionHandler(faultInjectionProperties); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/MockInstanceIsolationExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; public class MockInstanceIsolationExtension extends AbstractInstanceIsolationExtension { @Override protected String extractStatusCode(Object result) { return "200"; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/MockMicroserviceMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; public class MockMicroserviceMeta implements MicroserviceMeta { @Override public String getName() { return "myself"; } @Override public String getVersion() { return "1.0"; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; public class MockRetryExtension extends AbstractRetryExtension { @Override public boolean isFailedResult(List statusList, Object result) { return false; } @Override protected String extractStatusCode(Object result) { return null; } @Override public boolean isFailedResult(List statusList, Throwable e) { return false; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/OperatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.time.Duration; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.governance.marker.operator.RawOperator; import org.apache.servicecomb.governance.policy.RetryPolicy; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class OperatorTest { private RequestProcessor requestProcessor; @Autowired public void setRequestProcessor(RequestProcessor requestProcessor) { this.requestProcessor = requestProcessor; } @Test public void test_unknown_operator() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/test"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("unknown", "/test"); matcher.setApiPath(apiPath); Assertions.assertFalse(requestProcessor.match(request, matcher)); } @Test public void test_exact_api_path_match() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("exact", "/bulkhead"); matcher.setApiPath(apiPath); Assertions.assertTrue(requestProcessor.match(request, matcher)); } @Test public void test_prefix_api_path_match() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead/hello"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("prefix", "/bulkhead"); matcher.setApiPath(apiPath); Assertions.assertTrue(requestProcessor.match(request, matcher)); } @Test public void test_prefix_api_path_not_match_null() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead/hello"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("prefix", null); matcher.setApiPath(apiPath); Assertions.assertFalse(requestProcessor.match(request, matcher)); } @Test public void test_suffix_api_path_match() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/api/bulkhead"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("suffix", "/bulkhead"); matcher.setApiPath(apiPath); Assertions.assertTrue(requestProcessor.match(request, matcher)); } @Test public void test_suffix_api_path_not_match_null() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/api/bulkhead"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("suffix", null); matcher.setApiPath(apiPath); Assertions.assertFalse(requestProcessor.match(request, matcher)); } @Test public void test_exact_api_path_not_match() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead/"); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("exact", "/bulkhead"); matcher.setApiPath(apiPath); Assertions.assertFalse(requestProcessor.match(request, matcher)); request.setApiPath("/bulkhead"); request.setApiPath(null); Assertions.assertFalse(requestProcessor.match(request, matcher)); request.setApiPath("/bulkhead"); apiPath.clear(); matcher.setApiPath(apiPath); Assertions.assertFalse(requestProcessor.match(request, matcher)); } @Test public void test_exact_api_path_match_header_match() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead"); request.setMethod("GET"); Map reqHeaders = new HashMap<>(); reqHeaders.put("header1", "value1"); request.setHeaders(reqHeaders); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("exact", "/bulkhead"); matcher.setApiPath(apiPath); matcher.setMethod(Arrays.asList("GET")); Map headers = new HashMap<>(); RawOperator header1 = new RawOperator(); header1.put("exact", "value1"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); } @Test public void test_exact_api_path_match_header_not_match() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/bulkhead"); request.setMethod("GET"); Map reqHeaders = new HashMap<>(); reqHeaders.put("header1", "value2"); request.setHeaders(reqHeaders); Matcher matcher = new Matcher(); RawOperator apiPath = new RawOperator(); apiPath.put("exact", "/bulkhead"); matcher.setApiPath(apiPath); matcher.setMethod(Arrays.asList("GET")); Map headers = new HashMap<>(); RawOperator header1 = new RawOperator(); header1.put("exact", "value1"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertFalse(requestProcessor.match(request, matcher)); reqHeaders.clear(); request.setHeaders(reqHeaders); Assertions.assertFalse(requestProcessor.match(request, matcher)); } @Test public void test_header_low_case() { GovernanceRequest request = new GovernanceRequest(); Map reqHeaders = new HashMap<>(); reqHeaders.put("hEadeR", "100"); request.setHeaders(reqHeaders); Matcher matcher = new Matcher(); Map headers = new HashMap<>(); RawOperator header1 = new RawOperator(); header1.put("compare", ">10"); headers.put("HeAder", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); } @Test public void test_compare_header_match() { GovernanceRequest request = new GovernanceRequest(); Map reqHeaders = new HashMap<>(); reqHeaders.put("header1", "100"); request.setHeaders(reqHeaders); Matcher matcher = new Matcher(); Map headers = new HashMap<>(); RawOperator header1 = new RawOperator(); header1.put("compare", ">10"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", ">=10"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", "<1000"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", "<=1000"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", "=100"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertTrue(requestProcessor.match(request, matcher)); } @Test public void test_compare_header_not_match() { GovernanceRequest request = new GovernanceRequest(); Map reqHeaders = new HashMap<>(); reqHeaders.put("header1", "100"); request.setHeaders(reqHeaders); Matcher matcher = new Matcher(); Map headers = new HashMap<>(); RawOperator header1 = new RawOperator(); header1.put("compare", ">1000"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertFalse(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", ">=1000"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertFalse(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", "<10"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertFalse(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", "<=10"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertFalse(requestProcessor.match(request, matcher)); header1 = new RawOperator(); header1.put("compare", "=200"); headers.put("header1", header1); matcher.setHeaders(headers); Assertions.assertFalse(requestProcessor.match(request, matcher)); } @Test public void test_time_changed_to_duration() { RetryPolicy retryPolicy = new RetryPolicy(); String result; result = retryPolicy.stringOfDuration("100", Duration.ofMillis(10)); Assertions.assertEquals("PT0.1S", result); Assertions.assertEquals(100, Duration.parse(result).toMillis()); result = retryPolicy.stringOfDuration("3S", Duration.ofMillis(10)); Assertions.assertEquals("PT3S", result); Assertions.assertEquals(3000, Duration.parse(result).toMillis()); result = retryPolicy.stringOfDuration("1M", Duration.ofMillis(10)); Assertions.assertEquals("PT1M", result); Assertions.assertEquals(60000, Duration.parse(result).toMillis()); result = retryPolicy.stringOfDuration("1H", Duration.ofMillis(10)); Assertions.assertEquals("PT1H", result); Assertions.assertEquals(3600000, Duration.parse(result).toMillis()); result = retryPolicy.stringOfDuration("3", Duration.ofMillis(10)); Assertions.assertEquals("PT0.003S", result); Assertions.assertEquals(3, Duration.parse(result).toMillis()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import org.apache.servicecomb.governance.handler.Disposable; import org.apache.servicecomb.governance.handler.RetryHandler; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.policy.RetryPolicy; import org.apache.servicecomb.governance.properties.RetryProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.github.resilience4j.retry.MaxRetriesExceededException; import io.github.resilience4j.retry.Retry; public class RetryHandlerTest { @Test public void testNotFailAfterMaxAttemptsWhenThrow() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); retryPolicy.setFailAfterMaxAttempts(false); RetryHandler retryHandler = new RetryHandler(retryProperties, retryExtension); Disposable retry = retryHandler.createProcessor(retryPolicy.getName(), governanceRequest, retryPolicy); Assertions.assertThrows(IllegalStateException.class, () -> retry.get().executeCheckedSupplier(() -> { throw new IllegalStateException(); })); } @Test public void testFailAfterMaxAttemptsWhenThrow() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); retryPolicy.setFailAfterMaxAttempts(true); RetryHandler retryHandler = new RetryHandler(retryProperties, retryExtension); Disposable retry = retryHandler.createProcessor(retryPolicy.getName(), governanceRequest, retryPolicy); Assertions.assertThrows(IllegalStateException.class, () -> retry.get().executeCheckedSupplier(() -> { throw new IllegalStateException(); })); } @Test public void testFailAfterMaxAttemptsOnResult() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); Mockito.when(retryExtension.isFailedResult(Mockito.any(), (Object) Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); retryPolicy.setFailAfterMaxAttempts(true); RetryHandler retryHandler = new RetryHandler(retryProperties, retryExtension); Disposable retry = retryHandler.createProcessor(retryPolicy.getName(), governanceRequest, retryPolicy); Assertions.assertThrows(MaxRetriesExceededException.class, () -> retry.get().executeCheckedSupplier(() -> -1)); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/TimeLimiterHandlerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance; import java.time.Duration; import org.apache.servicecomb.governance.handler.TimeLimiterHandler; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import io.github.resilience4j.timelimiter.TimeLimiter; @SpringBootTest @ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) public class TimeLimiterHandlerTest { private TimeLimiterHandler timeLimiterHandler; @Autowired public void setInstanceIsolationHandler(TimeLimiterHandler timeLimiterHandler) { this.timeLimiterHandler = timeLimiterHandler; } @Test public void testMatchPriorityPolicy() { GovernanceRequest request = new GovernanceRequest(); request.setApiPath("/timeLimiter"); TimeLimiterPolicy policy = timeLimiterHandler.matchPolicy(request); Assertions.assertEquals("demo-timeLimiter", policy.getName()); TimeLimiter timeLimiter = timeLimiterHandler.getActuator(request); Duration timeoutDuration = timeLimiter.getTimeLimiterConfig().getTimeoutDuration(); Assertions.assertEquals(2000, timeoutDuration.toMillis()); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.handler.ext; import java.io.IOException; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.net.SocketTimeoutException; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import io.vertx.core.VertxException; @SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RetryExtensionTest { @Test public void test_status_code_to_contains() { List statusList = Arrays.asList("502", "503"); boolean result = AbstractRetryExtension.statusCodeContains(statusList, "502"); Assertions.assertTrue(result); result = AbstractRetryExtension.statusCodeContains(statusList, "504"); Assertions.assertFalse(result); statusList = Arrays.asList("5xx", "4x4", "4x", "x32", "xx6"); result = AbstractRetryExtension.statusCodeContains(statusList, "502"); Assertions.assertTrue(result); result = AbstractRetryExtension.statusCodeContains(statusList, "504"); Assertions.assertTrue(result); statusList = Arrays.asList("4x4", "x32", "xx6"); result = AbstractRetryExtension.statusCodeContains(statusList, "402"); Assertions.assertFalse(result); result = AbstractRetryExtension.statusCodeContains(statusList, "404"); Assertions.assertTrue(result); result = AbstractRetryExtension.statusCodeContains(statusList, "332"); Assertions.assertTrue(result); result = AbstractRetryExtension.statusCodeContains(statusList, "446"); Assertions.assertTrue(result); statusList = Arrays.asList("4x", "x3x", "x5"); result = AbstractRetryExtension.statusCodeContains(statusList, "446"); Assertions.assertFalse(result); result = AbstractRetryExtension.statusCodeContains(statusList, "455"); Assertions.assertFalse(result); result = AbstractRetryExtension.statusCodeContains(statusList, "434"); Assertions.assertTrue(result); } @Test public void testRetryWithConnectionException() { Exception target = new ConnectException("connection refused"); Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); } @Test public void testRetryWithSocketTimeout() { Exception target = new SocketTimeoutException("Read timed out"); Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); } @Test public void testRetryWithIOException() { Exception target = new IOException("Connection reset by peer"); Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); target = new IOException("Target not exist"); root = new Exception(target); canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertFalse(canRetry); } @Test public void testRetryVertxException() { Exception target = new VertxException("Connection was closed"); Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); target = new IOException(""); root = new Exception(target); canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertFalse(canRetry); } @Test public void testRetryNoRouteToHostException() { Exception target = new NoRouteToHostException("Host is unreachable"); Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); target = new NoRouteToHostException("No route to host"); root = new Exception(target); canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); } @Test public void testRetryEqualTen() { Exception target = new ConnectException("connection refused"); for (int i = 0; i < 8; i++) { target = new Exception("Level" + i, target); } Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertTrue(canRetry); } @Test public void testRetryOverTen() { Exception target = new ConnectException("connection refused"); for (int i = 0; i < 9; i++) { target = new Exception("Level" + i, target); } Exception root = new Exception(target); boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); Assertions.assertFalse(canRetry); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.mockclasses; import org.springframework.stereotype.Component; import org.apache.servicecomb.governance.marker.GovernanceRequest; @Component public class ClassNotImplements { public boolean matchRequest(GovernanceRequest request, String parameters) { return true; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassWithAnnotation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.mockclasses; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.mockclasses.service.MockProfileClassUserService; import org.apache.servicecomb.governance.utils.CustomMatch; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ClassWithAnnotation implements CustomMatch { @Autowired MockProfileClassUserService service; @Override public boolean matchRequest(GovernanceRequestExtractor requestExtractor, String parameters) { String profileValue = service.getUser(); return Stream.of(parameters.split(",")).collect(Collectors.toSet()).contains(profileValue); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/mockclasses/CustomMatchDemo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.mockclasses; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.utils.CustomMatch; public class CustomMatchDemo implements CustomMatch { private CustomMatchDemo() { } @Override public boolean matchRequest(GovernanceRequestExtractor requestExtractor, String parameters) { return true; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockConfigurationForCustomMatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.mockclasses.service; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("org.apache.servicecomb.governance.mockclasses") public class MockConfigurationForCustomMatcher { } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockProfileClassUserService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.governance.mockclasses.service; import org.springframework.stereotype.Service; @Service public class MockProfileClassUserService { public String getUser() { return "bill"; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import org.apache.servicecomb.router.distribute.AbstractRouterDistributor; import org.springframework.stereotype.Component; @Component public class ExampleDistributor extends AbstractRouterDistributor { public ExampleDistributor() { init(ServiceIns::getVersion, ServiceIns::getServerName, ServiceIns::getTags); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorDynamicConfig2Test { private static final String TARGET_SERVICE_NAME = "test_server"; public static final String CONFIG_KEY = RouterRuleCache.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME; private static final String RULE_STRING = "" + " - precedence: 2\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 1\n" + " match:\n" + " headers:\n" + " appId:\n" + " regex: 01\n" + " caseInsensitive: false\n" + " userId:\n" + " exact: 01\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; private Environment environment; private RouterFilter routerFilter; private RouterDistributor testDistributor; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setRouterFilter(RouterFilter routerFilter) { this.routerFilter = routerFilter; } @Autowired public void setTestDistributor(RouterDistributor testDistributor) { this.testDistributor = testDistributor; } private final Map dynamicValues = new HashMap<>(); @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; if (configurableEnvironment.getPropertySources().contains("testDynamicChange")) { configurableEnvironment.getPropertySources().remove("testDynamicChange"); } configurableEnvironment.getPropertySources() .addFirst(new EnumerablePropertySource>("testDynamicChange", dynamicValues) { @Override public Object getProperty(String s) { return this.getSource().get(s); } @Override public String[] getPropertyNames() { return this.getSource().keySet().toArray(new String[0]); } }); dynamicValues.put(CONFIG_KEY, RULE_STRING); postConfigurationChangedEvent(); } private void postConfigurationChangedEvent() { Set changedKeys = new HashSet<>(); changedKeys.add(CONFIG_KEY); GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); GovernanceEventManager.post(newEvent); } @Test public void testMatchPrecedenceHigher() { GovernanceRequest governanceRequest = new GovernanceRequest(); Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("2.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.setVersion("1.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @Test public void testCaseSensitiveNotMatch() { String rule = "" + " - precedence: 1\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 2\n" + " match:\n" + " headers:\n" + " userId:\n" + " exact: user01\n" + " caseInsensitive: false\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; dynamicValues.put(CONFIG_KEY, rule); postConfigurationChangedEvent(); Map headers = new HashMap<>(); headers.put("userId", "User01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("2.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.setVersion("1.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); Assertions.assertEquals("2.0", resultServerList.get(0).getVersion()); } @Test public void testNoneMatch() { String rule = "" + " - precedence: 1\n" + " match:\n" + " headers:\n" + " userId:\n" + " regex: user02\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 2\n" + " match:\n" + " headers:\n" + " userId:\n" + " exact: user01\n" + " caseInsensitive: false\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; dynamicValues.put(CONFIG_KEY, rule); postConfigurationChangedEvent(); Map headers = new HashMap<>(); headers.put("userId", "User01"); headers.put("appId", "App01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("2.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.setVersion("1.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } @Test public void testOneMatchButNoInstance() { String rule = "" + " - precedence: 1\n" + " match:\n" + " headers:\n" + " appId:\n" + " regex: app02\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 2\n" + " match:\n" + " headers:\n" + " userId:\n" + " exact: user01\n" + " caseInsensitive: false\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; dynamicValues.put(CONFIG_KEY, rule); postConfigurationChangedEvent(); Map headers = new HashMap<>(); headers.put("userId", "user01"); headers.put("appId", "app02"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("1.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.setVersion("1.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } private List mainFilter(List serverList, GovernanceRequestExtractor requestExtractor) { return routerFilter .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, requestExtractor, testDistributor); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorDynamicConfigTest { private static final String TARGET_SERVICE_NAME = "test_server"; private static final String RULE_STRING = "" + " - precedence: 2 #优先级\n" + " match: #匹配策略\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"01\"\n" + " route: #路由规则\n" + " - weight: 50\n" + " tags:\n" + " version: 1.1\n" + " - precedence: 1\n" + " match:\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 3\n" + " match:\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; private Environment environment; private RouterFilter routerFilter; private RouterDistributor testDistributor; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setRouterFilter(RouterFilter routerFilter) { this.routerFilter = routerFilter; } @Autowired public void setTestDistributor(RouterDistributor testDistributor) { this.testDistributor = testDistributor; } public RouterDistributorDynamicConfigTest() { } private final Map dynamicValues = new HashMap<>(); @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; if (configurableEnvironment.getPropertySources().contains("testDynamicChange")) { configurableEnvironment.getPropertySources().remove("testDynamicChange"); } configurableEnvironment.getPropertySources() .addFirst(new EnumerablePropertySource>("testDynamicChange", dynamicValues) { @Override public Object getProperty(String s) { return this.getSource().get(s); } @Override public String[] getPropertyNames() { return this.getSource().keySet().toArray(new String[0]); } }); dynamicValues.put(RouterRuleCache.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME, RULE_STRING); Set changedKeys = new HashSet<>(); changedKeys.add(RouterRuleCache.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME); GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); GovernanceEventManager.post(newEvent); } @Test public void testHeaderIsEmpty() { List list = getMockList(); List serverList = mainFilter(list, new GovernanceRequest()); Assertions.assertEquals(2, serverList.size()); } @Test public void testVersionNotMatch() { Map headerMap = new HashMap<>(); headerMap.put("userId", "02"); headerMap.put("appId", "01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headerMap); List list = getMockList(); list.remove(1); List serverList = mainFilter(list, governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @Test public void testVersionMatch() { Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("02", serverList.get(0).getId()); } @Test public void testMatchPrecedenceLower() { Map headers = new HashMap<>(); headers.put("userId", "02"); headers.put("appId", "01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @Test public void testMatchPrecedenceHigher() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("2.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.addTags("app", "a"); ins2.setVersion("1.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } private List getMockList() { List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("2.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.addTags("app", "a"); serverList.add(ins1); serverList.add(ins2); return serverList; } private List mainFilter(List serverList, GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorFileConfigTest { private static final String TARGET_SERVICE_NAME = "test_server1"; private RouterFilter routerFilter; private RouterDistributor routerDistributor; @Autowired public void setRouterFilter(RouterFilter routerFilter) { this.routerFilter = routerFilter; } @Autowired public void setRouterDistributor(RouterDistributor routerDistributor) { this.routerDistributor = routerDistributor; } @Test public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(header); List listOfServers = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); } int serverNum1 = 0; int serverNum2 = 0; for (int i = 0; i < 10; i++) { List serverList = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; } else if ("02".equals(serviceIns.getId())) { serverNum2++; } } } boolean flag = false; if (Math.round(serverNum2 * 1.0 / serverNum1) == 4) { flag = true; } Assertions.assertTrue(flag); } List initServiceList() { ServiceIns serviceIns1 = new ServiceIns("01", "test_server1"); ServiceIns serviceIns2 = new ServiceIns("02", "test_server1"); serviceIns1.setVersion("1.0"); serviceIns2.setVersion("2.0"); serviceIns1.addTags("x-group", "red"); serviceIns2.addTags("x-group", "green"); List list = new ArrayList<>(); list.add(serviceIns1); list.add(serviceIns2); return list; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorFileWeightLessTest { private static final String TARGET_SERVICE_NAME = "test_server2"; private RouterFilter routerFilter; private RouterDistributor routerDistributor; @Autowired public void setRouterFilter(RouterFilter routerFilter) { this.routerFilter = routerFilter; } @Autowired public void setRouterDistributor(RouterDistributor routerDistributor) { this.routerDistributor = routerDistributor; } @Test public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(header); List listOfServers = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); } int serverNum1 = 0; int unSetTagNum = 0; for (int i = 0; i < 10; i++) { List serverList = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; } else if ("02".equals(serviceIns.getId())) { unSetTagNum++; } } } boolean flag = false; if (Math.round(unSetTagNum * 1.0 / serverNum1) == 4) { flag = true; } Assertions.assertTrue(flag); } List initServiceList() { ServiceIns serviceIns1 = new ServiceIns("01", "test_server2"); ServiceIns serviceIns2 = new ServiceIns("02", "test_server2"); serviceIns1.setVersion("1.0"); serviceIns2.setVersion("2.0"); serviceIns1.addTags("x-group", "red"); serviceIns2.addTags("x-group", "green"); List list = new ArrayList<>(); list.add(serviceIns1); list.add(serviceIns2); return list; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorFileWithFallbackTest { private static final String TARGET_SERVICE_NAME_WITHOUT_FALLBACK = "test_server3"; private static final String TARGET_SERVICE_NAME_WITH_FALLBACK = "test_server4"; private static final String TARGET_SERVICE_NAME_ROUTE_FALLBACK = "test_server5"; private RouterFilter routerFilter; private RouterDistributor routerDistributor; @Autowired public void setRouterFilter(RouterFilter routerFilter) { this.routerFilter = routerFilter; } @Autowired public void setRouterDistributor(RouterDistributor routerDistributor) { this.routerDistributor = routerDistributor; } @Test public void testDistributeWithoutFallback() { List list = initServiceList(TARGET_SERVICE_NAME_WITHOUT_FALLBACK); HashMap header = new HashMap<>(); header.put("canary", "canary"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(header); List listOfServers = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITHOUT_FALLBACK, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME_WITHOUT_FALLBACK, server.getServerName()); } int serverNum1 = 0; int serverNum2 = 0; for (int i = 0; i < 10; i++) { List serverList = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITHOUT_FALLBACK, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; } else if ("02".equals(serviceIns.getId())) { serverNum2++; } } } Assertions.assertTrue(serverNum2 == serverNum1); } @Test public void testDistributeWithFallback() { List list = initServiceList(TARGET_SERVICE_NAME_WITH_FALLBACK); HashMap header = new HashMap<>(); header.put("canary", "canary"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(header); List listOfServers = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITH_FALLBACK, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME_WITH_FALLBACK, server.getServerName()); } int serverNum1 = 0; int serverNum2 = 0; for (int i = 0; i < 10; i++) { List serverList = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITH_FALLBACK, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; } else if ("02".equals(serviceIns.getId())) { serverNum2++; } } } Assertions.assertTrue((serverNum2 + serverNum1) == serverNum1); } @Test public void testDistributeRouteAndFallbackHaveSame() { List list = initServiceList(TARGET_SERVICE_NAME_ROUTE_FALLBACK); HashMap header = new HashMap<>(); header.put("canary", "canary"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(header); List listOfServers = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME_ROUTE_FALLBACK, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME_ROUTE_FALLBACK, server.getServerName()); } int serverNum1 = 0; int serverNum2 = 0; for (int i = 0; i < 20; i++) { List serverList = routerFilter .getFilteredListOfServers(list, TARGET_SERVICE_NAME_ROUTE_FALLBACK, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; } else if ("02".equals(serviceIns.getId())) { serverNum2++; } } } boolean flag = false; if (Math.round(serverNum1 * 1.0 / serverNum2) == 3) { flag = true; } Assertions.assertTrue(flag); } List initServiceList(String serviceName) { ServiceIns serviceIns1 = new ServiceIns("01", serviceName); ServiceIns serviceIns2 = new ServiceIns("02", serviceName); serviceIns1.setVersion("1.0"); serviceIns2.setVersion("2.0"); serviceIns1.addTags("x-group", "red"); serviceIns2.addTags("x-group", "green"); List list = new ArrayList<>(); list.add(serviceIns1); list.add(serviceIns2); return list; } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; @SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorGlobalConfigTest { private static final String TARGET_SERVICE_NAME = "test_server"; public static final String CONFIG_KEY = RouterRuleCache.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME; private static final String RULE_STRING = "" + " - precedence: 1\n" + " match:\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 2\n" + " match:\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; private Environment environment; private RouterFilter routerFilter; private RouterDistributor testDistributor; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setRouterFilter(RouterFilter routerFilter) { this.routerFilter = routerFilter; } @Autowired public void setTestDistributor(RouterDistributor testDistributor) { this.testDistributor = testDistributor; } public RouterDistributorGlobalConfigTest() { } private final Map dynamicValues = new HashMap<>(); @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; if (configurableEnvironment.getPropertySources().contains("testDynamicChange")) { configurableEnvironment.getPropertySources().remove("testDynamicChange"); } configurableEnvironment.getPropertySources() .addFirst(new EnumerablePropertySource>("testDynamicChange", dynamicValues) { @Override public Object getProperty(String s) { return this.getSource().get(s); } @Override public String[] getPropertyNames() { return this.getSource().keySet().toArray(new String[0]); } }); dynamicValues.put(RouterRuleCache.GLOBAL_ROUTE_RULE_KEY, RULE_STRING); postConfigurationChangedEvent(RouterRuleCache.GLOBAL_ROUTE_RULE_KEY); } @Test public void testUseGlobalRouteRule() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("1.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.addTags("app", "a"); ins2.setVersion("2.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @Test public void testUseProviderRouteRule() { String rule = "" + " - precedence: 1\n" + " match:\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 2.0\n" + " - precedence: 2\n" + " match:\n" + " headers: #header匹配\n" + " appId:\n" + " exact: \"01\"\n" + " userId:\n" + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" + " version: 1.0\n"; dynamicValues.put(CONFIG_KEY, rule); postConfigurationChangedEvent(CONFIG_KEY); Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); GovernanceRequest governanceRequest = new GovernanceRequest(); governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); ins1.setVersion("1.0"); ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); ins2.addTags("app", "a"); ins2.setVersion("2.0"); serverList.add(ins1); serverList.add(ins2); List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } private List mainFilter(List serverList, GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } private void postConfigurationChangedEvent(String changKey) { Set changedKeys = new HashSet<>(); changedKeys.add(changKey); GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); GovernanceEventManager.post(newEvent); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/ServiceIns.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import java.util.HashMap; import java.util.Map; public class ServiceIns { String version = "1.1"; String serverName; Map tags = new HashMap<>(); private final String id; public ServiceIns(String id, String serverName) { this.id = id; this.serverName = serverName; } public String getId() { return id; } public String getVersion() { return version; } public String getServerName() { return serverName; } public Map getTags() { return tags; } public void setVersion(String version) { this.version = version; } public void addTags(String key, String v) { tags.put(key, v); } } ================================================ FILE: governance/src/test/java/org/apache/servicecomb/router/VersionCompareUtilTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router; import org.apache.servicecomb.router.util.VersionCompareUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class VersionCompareUtilTest { @Test public void testVersion() { Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.1", "0.0.0") > 0); Assertions.assertEquals(0, VersionCompareUtil.compareVersion("0.0.0", "0.0.0")); Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.0", "0.0.1") < 0); Assertions.assertEquals(0, VersionCompareUtil.compareVersion("0.0.0", "0.0.0.0")); Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.1", "0.0.0.0") > 0); Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.1", "0.0.0.0") > 0); } } ================================================ FILE: governance/src/test/resources/META-INF/spring/bean.xml ================================================ ================================================ FILE: governance/src/test/resources/application.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: matchGroup: demo-timeLimiter: | matches: - apiPath: prefix: "/timeLimiter" demo-governanceCache: | matches: - apiPath: prefix: "/governanceCache" demo-loadbalance-random: | matches: - serviceName: "loadrandom" demo-loadbalance-roundRobin: | matches: - serviceName: "loadroundRobin" demo-fallback-returnNull: | matches: - serviceName: "returnNull" demo-fallback-ThrowException: | matches: - serviceName: "ThrowException" demo-faultInject-forceClosed: | matches: - serviceName: "forceClosed" demo-allOperation: | matches: - apiPath: prefix: "/" demo-rateLimiting: | matches: - apiPath: exact: "/hello" demo-rateLimiting-servicename: | matches: - apiPath: exact: "/helloServiceName" serviceName: "srcService" wrong-name-inogred: | wrong: some demo-retry: | matches: - apiPath: exact: "/retry" demo-circuitBreaker: | matches: - apiPath: exact: "/circuitBreaker" demo-bulkhead: | matches: - name: matchPath apiPath: exact: "/bulkhead" - name: matchHeader headers: x-token: exact: token demo-bulkhead-priority: | matches: - apiPath: exact: "/bulkhead" demo-bulkhead-other: | matches: - apiPath: exact: "/bulkhead" services: other:1.0 demo-faultInjectDelay: | matches: - apiPath: contains: "/faultInjectDelay" demo-faultInjectAbort: | matches: - apiPath: contains: "/faultInjectAbort" demo-mapper: | matches: - apiPath: prefix: "/mapper" demo-mapper-query: | matches: - queries: name: exact: "bob" rateLimiting: demo-rateLimiting: | rate: 1 demo-rateLimiting-servicename: | rate: 1 wrongIgnored: | rate: 0 identifierRateLimiting: demo-rateLimiting: | rate: 1 identifier: test demo-rateLimiting-servicename: | rate: 1 identifier: test timeLimiter: demo-timeLimiter: | timeoutDuration: 2000 cancelRunningFuture: true demo-timeLimiter-other: | timeoutDuration: 2000 cancelRunningFuture: false cache: demo-governanceCache: | ttl: 36000000 maximumSize: 50000 concurrencyLevel: 10 demo-governanceCache-other: | ttl: 666666 maximumSize: 50000 concurrencyLevel: 15 retry: demo-retry: | maxAttempts: 3 wrongIgnored: | maxAttempts: -1 circuitBreaker: demo-circuitBreaker: | minimumNumberOfCalls: 2 slidingWindowSize: 2 slidingWindowType: COUNT_BASED services: myself:1.0,other wrongIgnored: | rules: match: demo-circuitBreaker.xx minimumNumberOfCalls: -1 bulkhead: demo-bulkhead: | order: 1 maxConcurrentCalls: 1 maxWaitDuration: 3000 services: myself:1.0 demo-bulkhead-priority: | order: 0 maxConcurrentCalls: 1 maxWaitDuration: 3000 services: myself:1.0 demo-bulkhead-other: | maxConcurrentCalls: 1 maxWaitDuration: 3000 services: other:1.0 wrongIgnored: | rules: match: demo-bulkhead.xx maxWaitDuration: -1 instanceIsolation: demo-allOperation: | minimumNumberOfCalls: 2 slidingWindowSize: 2 slidingWindowType: COUNT_BASED waitDurationInOpenState: 1000 instanceBulkhead: demo-allOperation: | maxConcurrentCalls: 2 maxWaitDuration: 10 faultInjection: demo-fallback-ThrowException: | type: abort percentage: 100 demo-fallback-returnNull: | fallbackType: ReturnNull type: abort percentage: 100 demo-faultInject-forceClosed: | forceClosed: true fallbackType: ReturnNull demo-faultInjectDelay: | delayTime: 2S type: delay percentage: 100 demo-faultInjectAbort: | type: abort percentage: 50 errorCode: 500 wrongIgnored: | delayTime: -1 type: ERROR faultInjection2: demo-fallback-ThrowException: | type: abort percentage: 100 loadbalance: demo-loadbalance-random: | rule: Random demo-loadbalance-roundRobin: | rule: RoundRobin mapper: demo-mapper: | target: host: 127.0.0.1 port: 8080 demo-mapper-query: | target: user-id: $Q{name} mapper2: demo-mapper: | target: host: 127.0.0.1 port: 9090 routeRule: test_server1: | # 服务名 - precedence: 1 # 优先级,数字越大,优先级越高。 match: # 请求匹配规则。0..N个,不配置表示匹配。 headers: # header 匹配 region: # 如果配置了多个 header,那么所有的 header 规则都必须和请求匹配 exact: 'providerRegion' type: regex: gray_[a-z]+ # java 正则表达式匹配 route: # 路由规则 - weight: 20 # 权重值 tags: version: 1.0.0 # 实例标记。满足标记条件的实例放到这一组。 - weight: 80 # 权重值 tags: version: 2.0.0 # 实例标记。满足标记条件的实例放到这一组。 - precedence: 2 route: - weight: 20 tags: x-group: red - weight: 80 tags: x-group: green test_server2: | # 服务名 - precedence: 1 route: - weight: 20 tags: x-group: red test_server3: | # 服务名 - precedence: 2 match: headers: canary: exact: 'canary' route: - weight: 20 tags: version: 1.0.0 - weight: 80 tags: version: 2.0.0 test_server4: | # 服务名 - precedence: 2 match: headers: canary: exact: 'canary' route: - weight: 20 tags: version: 1.0.0 - weight: 80 tags: version: 2.0.0 fallback: - weight: 100 tags: x-group: red test_server5: | # 服务名 - precedence: 2 match: headers: canary: exact: 'canary' route: - weight: 50 tags: x-group: red fallback: - weight: 50 tags: x-group: red - weight: 50 tags: x-group: green ================================================ FILE: handlers/handler-fault-injection/pom.xml ================================================ 4.0.0 org.apache.servicecomb handlers 3.4.0-SNAPSHOT handler-fault-injection Java Chassis::Handlers::Fault Injection org.apache.servicecomb java-chassis-core io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ConsumerAbortFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerAbortFaultFilter.class); private static final String SUCCESS_RESPONSE = "success"; public static final String ABORTED_ERROR_MSG = "aborted by fault inject"; @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1020; } @Override public String getName() { return "consumer-abort-fault"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (!shouldAbort(invocation)) { return nextNode.onFilter(invocation); } // get the config values related to abort percentage. int errorCode = FaultInjectionUtil.getFaultInjectionConfig(invocation, "abort.httpStatus"); if (errorCode == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: Abort error code is not configured"); return CompletableFuture.completedFuture(Response.succResp(SUCCESS_RESPONSE)); } // if request need to be abort then return failure with given error code CommonExceptionData errorData = new CommonExceptionData(ABORTED_ERROR_MSG); return CompletableFuture.failedFuture(new InvocationException(errorCode, ABORTED_ERROR_MSG, errorData)); } private boolean shouldAbort(Invocation invocation) { // get the config values related to abort. int abortPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation, "abort.percent"); if (abortPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: Abort percentage is not configured"); return false; } // check fault abort condition. return FaultInjectionUtil.isFaultNeedToInject(abortPercent); } } ================================================ FILE: handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.Vertx; public class ConsumerDelayFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerDelayFaultFilter.class); @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1030; } @Override public String getName() { return "consumer-delay-fault"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (!shouldDelay(invocation)) { return nextNode.onFilter(invocation); } LOGGER.debug("Fault injection: delay is added for the request by fault inject handler"); long delay = FaultInjectionUtil.getFaultInjectionConfig(invocation, "delay.fixedDelay"); if (delay == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: delay is not configured"); return nextNode.onFilter(invocation); } return executeDelay(invocation, nextNode, delay); } private CompletableFuture executeDelay(Invocation invocation, FilterNode nextNode, long delay) { Context currentContext = Vertx.currentContext(); if (currentContext != null && currentContext.isEventLoopContext()) { CompletableFuture result = new CompletableFuture<>(); currentContext.owner().setTimer(delay, timeID -> nextNode.onFilter(invocation).whenComplete((r, e) -> { if (e == null) { result.complete(r); } else { result.completeExceptionally(e); } } )); return result; } try { Thread.sleep(delay); } catch (InterruptedException e) { LOGGER.info("Interrupted exception is received"); } return nextNode.onFilter(invocation); } private boolean shouldDelay(Invocation invocation) { int delayPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation, "delay.percent"); if (delayPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { LOGGER.debug("Fault injection: delay percentage is not configured"); return false; } // check fault delay condition. return FaultInjectionUtil.isFaultNeedToInject(delayPercent); } } ================================================ FILE: handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnProperty(value = FaultInjectionConfiguration.FAULT_INJECTION_ENABLED, havingValue = "true") public class FaultInjectionConfiguration { public static final String FAULT_INJECTION_PREFIX = "servicecomb.faultInjection"; public static final String FAULT_INJECTION_ENABLED = FAULT_INJECTION_PREFIX + ".enabled"; @Bean public ConsumerAbortFaultFilter scbConsumerAbortFaultFilter() { return new ConsumerAbortFaultFilter(); } @Bean public ConsumerDelayFaultFilter scbConsumerDelayFaultFilter() { return new ConsumerDelayFaultFilter(); } } ================================================ FILE: handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; /** * Handles the all constant values for fault injection. */ public class FaultInjectionConst { public static final int FAULT_INJECTION_DEFAULT_VALUE = -1; public static final int FAULT_INJECTION_ERROR = -1; public static final String CONSUMER_FAULTINJECTION = "servicecomb.governance.Consumer."; public static final String CONSUMER_FAULTINJECTION_GLOBAL = "servicecomb.governance.Consumer._global."; public static final String CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS = "policy.fault.protocols."; } ================================================ FILE: handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION; import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL; import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS; import static org.apache.servicecomb.faultinjection.FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import com.google.common.annotations.VisibleForTesting; /** * Handles the count for all request based key[transport + microservice qualified name]. */ public class FaultInjectionUtil { private FaultInjectionUtil() { } // key is config parameter private static final Map configCenterValue = new ConcurrentHashMapEx<>(); /** * Returns the map of config parameter key and values. * @return value of config parameter map */ public static Map getConfigCenterMap() { return configCenterValue; } /** * Sets the value for given config parameter. * @param key * @param value */ public static void setConfigCenterValue(String key, AtomicInteger value) { configCenterValue.put(key, value); } /** * Handles the reading fault injection configuration. * * @param invocation * invocation of request * @param key * configuration key * @return configuration value */ public static int getFaultInjectionConfig(Invocation invocation, String key) { int value = 0; String config; // get the config base on priority. operationName-->schema-->service-->global String operationName = invocation.getOperationName(); String schema = invocation.getSchemaId(); String serviceName = invocation.getMicroserviceName(); config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + ".operations." + operationName + "." + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; value = getConfigValue(config); if ((value != FAULT_INJECTION_DEFAULT_VALUE)) { return value; } config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + "." + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; value = getConfigValue(config); if ((value != FAULT_INJECTION_DEFAULT_VALUE)) { return value; } config = CONSUMER_FAULTINJECTION + serviceName + "." + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; value = getConfigValue(config); if ((value != FAULT_INJECTION_DEFAULT_VALUE)) { return value; } config = CONSUMER_FAULTINJECTION_GLOBAL + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; value = getConfigValue(config); return value; } /** * Get the configuration value * @param config config parameter * @return int value */ private static int getConfigValue(String config) { int value = 0; //first need to check in config center map which has high priority. Map cfgMap = FaultInjectionUtil.getConfigCenterMap(); if (cfgMap.containsKey(config)) { return cfgMap.get(config).get(); } value = LegacyPropertyFactory.getIntProperty(config, FAULT_INJECTION_DEFAULT_VALUE); return value; } /** * It will check the delay/abort condition based on percentage. * * @return true: delay/abort is needed. false: delay/abort is not needed. */ public static boolean isFaultNeedToInject(int percentage) { if (percentage > 0) { return ThreadLocalRandom.current().nextInt(100) < percentage; } return false; } @VisibleForTesting static Map getConfigCenterValue() { return configCenterValue; } } ================================================ FILE: handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import io.vertx.core.Vertx; /** * Fault injection parameters which decides the fault injection condition. */ public class FaultParam { private Vertx vertx; FaultParam() { } public Vertx getVertx() { return vertx; } public void setVertx(Vertx vertx) { this.vertx = vertx; } } ================================================ FILE: handlers/handler-fault-injection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.faultinjection.FaultInjectionConfiguration ================================================ FILE: handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerAbortFaultFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestConsumerAbortFaultFilter { private Invocation invocation; private Environment environment; @BeforeEach public void before() { environment = Mockito.mock(Environment.class); LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + ".policy.fault.protocols.rest.abort.percent", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + ".policy.fault.protocols.rest.abort.httpStatus", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + ".policy.fault.protocols.rest.abort.percent", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + ".policy.fault.protocols.rest.abort.httpStatus", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6" + ".policy.fault.protocols.rest.abort.percent", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6" + ".policy.fault.protocols.rest.abort.httpStatus", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.abort.percent", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.abort.httpStatus", int.class, -1)) .thenReturn(-1); FaultInjectionUtil.getConfigCenterValue().clear(); invocation = Mockito.mock(Invocation.class); Transport transport = Mockito.mock(Transport.class); Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName12"); Mockito.when(invocation.getTransport()).thenReturn(transport); Mockito.when(transport.getName()).thenReturn("rest"); Mockito.when(invocation.getOperationName()).thenReturn("sayBye4"); Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4"); Mockito.when(invocation.getMicroserviceName()).thenReturn("carts6"); } @AfterEach public void after() { } @AfterAll public static void classTeardown() { VertxUtils.blockCloseVertxByName("faultinjectionTest"); } @Test public void injectFaultError() { Mockito.when(environment.getProperty( "servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.abort.httpStatus", int.class, -1)) .thenReturn(421); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.abort.percent", int.class, -1)) .thenReturn(100); ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Holder resultHolder = new Holder<>(); CompletableFuture result = abortFault.onFilter(invocation, filterNode); result.whenComplete((r, e) -> { if (e != null) { resultHolder.value = (InvocationException) e; } }); Assertions.assertThrows(ExecutionException.class, () -> result.get()); Assertions.assertEquals(421, resultHolder.value.getStatusCode()); Assertions.assertEquals("aborted by fault inject", resultHolder.value.getReasonPhrase()); Assertions.assertEquals("CommonExceptionData [message=aborted by fault inject]", resultHolder.value.getErrorData().toString()); } @Test public void injectFaultNoError() throws Exception { ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = abortFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } @Test public void injectFaultNoPercentageConfig() throws Exception { ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = abortFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } @Test public void injectFaultNoErrorCodeConfig() throws Exception { ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = abortFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } } ================================================ FILE: handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerDelayFaultFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestConsumerDelayFaultFilter { private Invocation invocation; private Environment environment; @BeforeEach public void before() { environment = Mockito.mock(Environment.class); LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + ".policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4.operations.sayBye4" + ".policy.fault.protocols.rest.delay.percent", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + ".policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6.schemas.testSchema4" + ".policy.fault.protocols.rest.delay.percent", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6" + ".policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer.carts6" + ".policy.fault.protocols.rest.delay.percent", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty( "servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.delay.percent", int.class, -1)) .thenReturn(-1); FaultInjectionUtil.getConfigCenterValue().clear(); invocation = Mockito.mock(Invocation.class); Transport transport = Mockito.mock(Transport.class); Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName12"); Mockito.when(invocation.getTransport()).thenReturn(transport); Mockito.when(transport.getName()).thenReturn("rest"); Mockito.when(invocation.getOperationName()).thenReturn("sayBye4"); Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4"); Mockito.when(invocation.getMicroserviceName()).thenReturn("carts6"); } @AfterEach public void after() { } @AfterAll public static void classTeardown() { VertxUtils.blockCloseVertxByName("faultinjectionTest"); } @Test public void injectFaultVertxDelay() throws Exception { Mockito.when(environment.getProperty("servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)) .thenReturn(10); Mockito.when( environment.getProperty("servicecomb.governance.Consumer._global" + ".policy.fault.protocols.rest.delay.percent", int.class, -1)) .thenReturn(100); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = delayFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } @Test public void injectFaultSystemDelay() throws Exception { Mockito.when( environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)) .thenReturn(10); Mockito.when( environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", int.class, -1)) .thenReturn(100); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = delayFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } @Test public void injectFaultNotDelay() throws Exception { Mockito.when( environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", int.class, -1)) .thenReturn(10); Mockito.when( environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", int.class, -1)) .thenReturn(0); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = delayFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } @Test public void injectFaultNoPercentageConfig() throws Exception { ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = delayFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } @Test public void injectFaultNoDelayMsConfig() throws Exception { Mockito.when( environment.getProperty("servicecomb.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", int.class, -1)) .thenReturn(10); ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); FilterNode filterNode = Mockito.mock(FilterNode.class); Mockito.when(filterNode.onFilter(invocation)) .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); CompletableFuture result = delayFault.onFilter(invocation, filterNode); Assertions.assertEquals("success", result.get().getResult()); } } ================================================ FILE: handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.faultinjection; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests the fault inject util functionality. */ public class TestFaultInjectUtil { @Test public void testFaultInjectUtil() { FaultInjectionUtil.setConfigCenterValue("sayHi", new AtomicInteger(123)); int value = FaultInjectionUtil.getConfigCenterMap().get("sayHi").get(); Assertions.assertEquals(123, value); } } ================================================ FILE: handlers/handler-flowcontrol-qps/pom.xml ================================================ 4.0.0 org.apache.servicecomb handlers 3.4.0-SNAPSHOT handler-flowcontrol-qps Java Chassis::Handlers::Flow Control QPS org.apache.servicecomb java-chassis-core org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public final class Config { public static final String CONFIG_PREFIX = "servicecomb.flowcontrol."; public static final String STRATEGY_KEY = "servicecomb.flowcontrol.strategy"; public static final String ANY_SERVICE = "ANY"; public static final String CONSUMER_BUCKET_KEY_PREFIX = "servicecomb.flowcontrol.Consumer.qps.bucket."; public static final String PROVIDER_BUCKET_KEY_PREFIX = "servicecomb.flowcontrol.Provider.qps.bucket."; public static final String PROVIDER_BUCKET_KEY_GLOBAL = "servicecomb.flowcontrol.Provider.qps.global.bucket"; public static final String CONSUMER_BUCKET_KEY_GLOBAL = "servicecomb.flowcontrol.Consumer.qps.global.bucket"; public static final String CONSUMER_LIMIT_KEY_PREFIX = "servicecomb.flowcontrol.Consumer.qps.limit."; public static final String PROVIDER_LIMIT_KEY_PREFIX = "servicecomb.flowcontrol.Provider.qps.limit."; public static final String PROVIDER_LIMIT_KEY_GLOBAL = "servicecomb.flowcontrol.Provider.qps.global.limit"; public static final String CONSUMER_LIMIT_KEY_GLOBAL = "servicecomb.flowcontrol.Consumer.qps.global.limit"; public static final String CONSUMER_ENABLED = "servicecomb.flowcontrol.Consumer.qps.enabled"; public static final String PROVIDER_ENABLED = "servicecomb.flowcontrol.Provider.qps.enabled"; public static Config INSTANCE = new Config(); public Config() { } public boolean isConsumerEnabled() { return LegacyPropertyFactory.getBooleanProperty(CONSUMER_ENABLED, true); } public boolean isProviderEnabled() { return LegacyPropertyFactory.getBooleanProperty(PROVIDER_ENABLED, true); } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; public class ConsumerFlowControlFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private final QpsControllerManager qpsControllerMgr; public ConsumerFlowControlFilter(Environment environment) { qpsControllerMgr = new QpsControllerManager(false, environment); } @VisibleForTesting public QpsControllerManager getQpsControllerMgr() { return qpsControllerMgr; } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; } @Override public String getName() { return "consumer-flow-control"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (!Config.INSTANCE.isConsumerEnabled()) { return nextNode.onFilter(invocation); } QpsStrategy qpsStrategy = qpsControllerMgr.getOrCreate(invocation.getMicroserviceName(), invocation); if (qpsStrategy.isLimitNewRequest()) { CommonExceptionData errorData = new CommonExceptionData( "consumer request rejected by flow control."); return CompletableFuture.failedFuture(new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData)); } return nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration @ConditionalOnProperty(value = FlowControlQpsConfiguration.FLOW_CONTROL_ENABLED, havingValue = "true") public class FlowControlQpsConfiguration { public static final String FLOW_CONTROL_PREFIX = "servicecomb.flowcontrol"; public static final String FLOW_CONTROL_ENABLED = FLOW_CONTROL_PREFIX + ".enabled"; @Bean public ProviderFlowControlFilter scbProviderFlowControlFilter(Environment environment) { return new ProviderFlowControlFilter(environment); } @Bean public ConsumerFlowControlFilter scbConsumerFlowControlFilter(Environment environment) { return new ConsumerFlowControlFilter(environment); } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; public class ProviderFlowControlFilter extends AbstractFilter implements ProviderFilter { private final QpsControllerManager qpsControllerMgr; public ProviderFlowControlFilter(Environment environment) { qpsControllerMgr = new QpsControllerManager(true, environment); } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1990; } @Override public String getName() { return "provider-flow-control"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (!Config.INSTANCE.isProviderEnabled()) { return nextNode.onFilter(invocation); } String microserviceName = invocation.getContext(CoreConst.SRC_MICROSERVICE); QpsStrategy qpsStrategy = qpsControllerMgr.getOrCreate(microserviceName, invocation); if (qpsStrategy.isLimitNewRequest()) { CommonExceptionData errorData = new CommonExceptionData( "provider request rejected by flow control."); return CompletableFuture.failedFuture(new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData)); } return nextNode.onFilter(invocation); } @VisibleForTesting public QpsControllerManager getQpsControllerMgr() { return qpsControllerMgr; } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; public final class QpsConst { public static final StatusType TOO_MANY_REQUESTS_STATUS = new HttpStatus(429, "Too Many Requests"); private QpsConst() { } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; import org.apache.servicecomb.qps.strategy.IStrategyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; public final class QpsControllerManager { private static final Logger LOGGER = LoggerFactory.getLogger(QpsControllerManager.class); public static final String SEPARATOR = "."; /** * Describe the relationship between configuration and qpsController. */ private final Map configQpsControllerMap = new ConcurrentHashMapEx<>(); /** * Describe the relationship between qualifiedKey(format is "microservice.schema.operation") and qpsController. */ private final Map qualifiedNameControllerMap = new ConcurrentHashMapEx<>(); private AbstractQpsStrategy globalQpsStrategy; private final String limitKeyPrefix; private final String bucketKeyPrefix; private final String globalLimitKey; private final String globalBucketKey; private final Environment environment; public QpsControllerManager(boolean isProvider, Environment environment) { this.environment = environment; if (isProvider) { limitKeyPrefix = Config.PROVIDER_LIMIT_KEY_PREFIX; bucketKeyPrefix = Config.PROVIDER_BUCKET_KEY_PREFIX; globalLimitKey = Config.PROVIDER_LIMIT_KEY_GLOBAL; globalBucketKey = Config.PROVIDER_BUCKET_KEY_GLOBAL; } else { limitKeyPrefix = Config.CONSUMER_LIMIT_KEY_PREFIX; bucketKeyPrefix = Config.CONSUMER_BUCKET_KEY_PREFIX; globalLimitKey = Config.CONSUMER_LIMIT_KEY_GLOBAL; globalBucketKey = Config.CONSUMER_BUCKET_KEY_GLOBAL; } initGlobalQpsController(); EventManager.register(this); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(Config.CONFIG_PREFIX)) { configQpsControllerMap.clear(); qualifiedNameControllerMap.clear(); initGlobalQpsController(); break; } } } @VisibleForTesting public Map getQualifiedNameControllerMap() { return qualifiedNameControllerMap; } public QpsStrategy getOrCreate(String microserviceName, Invocation invocation) { final String name = validatedName(microserviceName); return qualifiedNameControllerMap .computeIfAbsent( name + SEPARATOR + invocation.getOperationMeta().getSchemaQualifiedName(), key -> create(key, name, invocation)); } private String validatedName(String microserviceName) { String name = microserviceName; if (StringUtils.isEmpty(microserviceName)) { name = Config.ANY_SERVICE; } return name; } /** * Create relevant qpsLimit dynamicProperty and watch the configuration change. * Search and return a valid qpsController. */ @VisibleForTesting AbstractQpsStrategy create(String qualifiedNameKey, String microserviceName, Invocation invocation) { createForService(qualifiedNameKey, microserviceName, invocation); String qualifiedAnyServiceName = Config.ANY_SERVICE + qualifiedNameKey.substring(microserviceName.length()); createForService(qualifiedAnyServiceName, Config.ANY_SERVICE, invocation); AbstractQpsStrategy strategy = searchQpsController(qualifiedNameKey); if (strategy == null) { strategy = searchQpsController(qualifiedAnyServiceName); } if (strategy == null) { return globalQpsStrategy; } return strategy; } private void createForService(String qualifiedNameKey, String microserviceName, Invocation invocation) { // create "microservice" createQpsControllerIfNotExist(microserviceName); // create "microservice.schema" createQpsControllerIfNotExist( qualifiedNameKey.substring(0, microserviceName.length() + invocation.getSchemaId().length() + 1)); // create "microservice.schema.operation" createQpsControllerIfNotExist(qualifiedNameKey); } /** *

Use qualifiedNameKey to search {@link QpsStrategy}. * Firstly try to search "microservice.schema.operation". If no valid result found, then try "microservice.schema", * and then "microservice" or global qpsController(If there is a global qpsController).

*

This method ensures that there is always an existing qpsController returned, as the relevant qpsController has * been created and stored in {@link #create(String, String, Invocation)}

* * @param qualifiedNameKey qualifiedNameKey in {@link #qualifiedNameControllerMap} * @return a qps controller, lower level controllers with valid qpsLimit have priority. */ private AbstractQpsStrategy searchQpsController(String qualifiedNameKey) { AbstractQpsStrategy qpsStrategy = configQpsControllerMap.get(qualifiedNameKey); if (isValidQpsController(qpsStrategy)) { return qpsStrategy; } int index = qualifiedNameKey.lastIndexOf(SEPARATOR); while (index > 0) { qpsStrategy = configQpsControllerMap.get(qualifiedNameKey.substring(0, index)); if (isValidQpsController(qpsStrategy)) { return qpsStrategy; } index = qualifiedNameKey.lastIndexOf(SEPARATOR, index - 1); } if (isValidQpsController(qpsStrategy)) { return qpsStrategy; } return null; } private boolean isValidQpsController(AbstractQpsStrategy qpsStrategy) { return null != qpsStrategy && null != qpsStrategy.getQpsLimit(); } private void createQpsControllerIfNotExist(String configKey) { if (configQpsControllerMap.containsKey(configKey)) { return; } LOGGER.info("Create qpsController, configKey = [{}]", configKey); AbstractQpsStrategy qpsStrategy = chooseStrategy(configKey, environment.getProperty(limitKeyPrefix + configKey, Long.class), environment.getProperty(bucketKeyPrefix + configKey, Long.class), environment.getProperty(Config.STRATEGY_KEY)); configQpsControllerMap.put(configKey, qpsStrategy); } private void initGlobalQpsController() { globalQpsStrategy = chooseStrategy(globalLimitKey, environment.getProperty(globalLimitKey, Long.class, (long) Integer.MAX_VALUE), environment.getProperty(globalBucketKey, Long.class), environment.getProperty(Config.STRATEGY_KEY)); } private AbstractQpsStrategy chooseStrategy(String configKey, Long limit, Long bucket, String strategyName) { if (StringUtils.isEmpty(strategyName)) { strategyName = "FixedWindow"; } AbstractQpsStrategy strategy = null; List strategyFactories = SPIServiceUtils .getOrLoadSortedService(IStrategyFactory.class); for (IStrategyFactory strategyFactory : strategyFactories) { strategy = strategyFactory.createStrategy(strategyName); if (strategy != null) { break; } } if (strategy == null) { throw new ServiceCombException( "the qps strategy name " + strategyName + " is not exist , please check."); } strategy.setKey(configKey); strategy.setQpsLimit(limit); strategy.setBucketLimit(bucket); return strategy; } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; public interface QpsStrategy { boolean isLimitNewRequest(); String name(); } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/AbstractQpsStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps.strategy; import org.apache.servicecomb.qps.QpsStrategy; public abstract class AbstractQpsStrategy implements QpsStrategy { private Long qpsLimit; private Long bucketLimit; private String key; public Long getBucketLimit() { return bucketLimit; } public void setBucketLimit(Long bucketLimit) { this.bucketLimit = bucketLimit; } @Override public abstract boolean isLimitNewRequest(); @Override public abstract String name(); public void setQpsLimit(Long qpsLimit) { this.qpsLimit = qpsLimit; } public Long getQpsLimit() { return qpsLimit; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/DefaultStrategyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps.strategy; public class DefaultStrategyFactory implements IStrategyFactory { public AbstractQpsStrategy createStrategy(String strategyName) { switch (strategyName) { case "TokenBucket": return new TokenBucketStrategy(); case "LeakyBucket": return new LeakyBucketStrategy(); case "FixedWindow": return new FixedWindowStrategy(); default: return null; } } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps.strategy; import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FixedWindowStrategy extends AbstractQpsStrategy { private static final Logger LOGGER = LoggerFactory.getLogger(FixedWindowStrategy.class); // Interval begin time private volatile long msCycleBegin; // Request count between Interval begin and now in one interval private final AtomicLong requestCount = new AtomicLong(); // request count before an interval private volatile long lastRequestCount = 1; private static final int CYCLE_LENGTH = 1000; private static final String STRATEGY_NAME = "FixedWindow"; // return true means new request need to be rejected public boolean isLimitNewRequest() { if (this.getQpsLimit() == null) { throw new IllegalStateException("should not happen"); } long newCount = requestCount.incrementAndGet(); long msNow = System.currentTimeMillis(); //Time jump cause the new request injected if (msNow - msCycleBegin > CYCLE_LENGTH || msNow < msCycleBegin) { //no need worry about concurrency problem lastRequestCount = newCount; msCycleBegin = msNow; } // Configuration update and use is at the situation of multi-thread concurrency // It is possible that operation level updated to null,but schema level or microservice level does not updated boolean isLimitRequest = newCount - lastRequestCount >= this.getQpsLimit(); if (isLimitRequest){ LOGGER.warn("qps flowcontrol open, qpsLimit is {} and tps is {}", this.getQpsLimit(), newCount - lastRequestCount + 1); } return isLimitRequest; } @Override public String name() { return STRATEGY_NAME; } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/IStrategyFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps.strategy; public interface IStrategyFactory { AbstractQpsStrategy createStrategy(String strategyName); } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/LeakyBucketStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps.strategy; import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * leaky bucket algorithm include 2 implementation : * 1. as a meter : it's same as the token bucket. * 2. as a queue : the bucket size equal to qpsLimit. * **/ public class LeakyBucketStrategy extends AbstractQpsStrategy { private static final Logger LOGGER = LoggerFactory.getLogger(LeakyBucketStrategy.class); // Request count between Interval begin and now in one interval private final AtomicLong requestCount = new AtomicLong(); private volatile long lastTime; private long remainder = 0; private static final String STRATEGY_NAME = "LeakyBucket"; @Override public boolean isLimitNewRequest() { if (this.getQpsLimit() == null) { throw new IllegalStateException("should not happen"); } if (this.getBucketLimit() == null) { this.setBucketLimit(Math.min(2 * this.getQpsLimit(), Integer.MAX_VALUE)); } long nowTime = System.currentTimeMillis(); //get the num of te period time long leakCount = ((nowTime - lastTime + remainder) / 1000) * this.getQpsLimit(); remainder = (nowTime - lastTime + remainder) % 1000; // leak the request if (requestCount.longValue() > leakCount) { requestCount.addAndGet(-leakCount); } else { requestCount.set(0); } lastTime = nowTime; //compute this time if (requestCount.longValue() < this.getBucketLimit()) { requestCount.incrementAndGet(); return false; } LOGGER.warn("qps flowcontrol open, qpsLimit is {} and tps is {}", this.getQpsLimit(), requestCount.longValue() + 1); return true; } @Override public String name() { return STRATEGY_NAME; } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/TokenBucketStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps.strategy; public class TokenBucketStrategy extends LeakyBucketStrategy { private static final String STRATEGY_NAME = "TokenBucket"; @Override public String name() { return STRATEGY_NAME; } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.qps.strategy.DefaultStrategyFactory ================================================ FILE: handlers/handler-flowcontrol-qps/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.qps.FlowControlQpsConfiguration ================================================ FILE: handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import java.util.HashMap; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class QpsControllerManagerTest { static Environment environment = Mockito.mock(Environment.class); @BeforeEach public void beforeTest() { Mockito.when(environment.getProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, Long.class, (long) Integer.MAX_VALUE)) .thenReturn((long) Integer.MAX_VALUE); Mockito.when(environment.getProperty(Config.CONSUMER_LIMIT_KEY_GLOBAL, Long.class, (long) Integer.MAX_VALUE)) .thenReturn((long) Integer.MAX_VALUE); } @AfterEach public void afterTest() { } @Test public void testGetOrCreate() { Invocation invocation = Mockito.mock(Invocation.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); QpsControllerManager testQpsControllerManager = new QpsControllerManager(false, environment); initTestQpsControllerManager(false, testQpsControllerManager, invocation, operationMeta); // pojo setConfigWithDefaultPrefix(false, "pojo", 100); QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); Mockito.when(environment.getProperty("servicecomb.flowcontrol.Consumer.qps.limit.poj.server", Long.class)).thenReturn(Long.valueOf(10000)); HashMap updated = new HashMap<>(); updated.put("servicecomb.flowcontrol.Consumer.qps.limit.poj.server", Long.valueOf(10000)); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals("poj.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(((AbstractQpsStrategy) qpsStrategy).getQpsLimit(), (Long) 10000L); InMemoryDynamicPropertiesSource.update("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", 20000); Mockito.when(environment.getProperty("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", Long.class)).thenReturn(Long.valueOf(20000)); updated = new HashMap<>(); updated.put("servicecomb.flowcontrol.Consumer.qps.limit.poj.server.test", Long.valueOf(20000)); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals("poj.server.test", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(((AbstractQpsStrategy) qpsStrategy).getQpsLimit(), (Long) 20000L); testGetOrCreateCommon(false, testQpsControllerManager, invocation, operationMeta); } @Test public void testGetOrCreateWithGlobalConfig() { Invocation invocation = Mockito.mock(Invocation.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); QpsControllerManager testQpsControllerManager = new QpsControllerManager(true, environment); // global Mockito.when(environment.getProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, Long.class, (long) Integer.MAX_VALUE)).thenReturn(50L); HashMap updated = new HashMap<>(); updated.put(Config.PROVIDER_LIMIT_KEY_GLOBAL, 50L); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); // pojo setConfigWithDefaultPrefix(true, "pojo", 100); qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); testGetOrCreateCommon(true, testQpsControllerManager, invocation, operationMeta); } @Test public void testQualifiedNameKey() { Invocation invocation = Mockito.mock(Invocation.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getSchemaId()).thenReturn("schema"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr"); QpsControllerManager qpsControllerManager = new QpsControllerManager(true, environment); QpsStrategy qpsStrategy = qpsControllerManager.getOrCreate("service", invocation); Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); Mockito.when(invocation.getSchemaId()).thenReturn("test_schema"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("test_schema.test_opr"); qpsStrategy = qpsControllerManager.getOrCreate("test_service", invocation); Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); Mockito.when(invocation.getSchemaId()).thenReturn("test_schema"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("test-schema.test-opr"); qpsStrategy = qpsControllerManager.getOrCreate("test-service", invocation); Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); Mockito.when(invocation.getSchemaId()).thenReturn("schema"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr.tail"); qpsStrategy = qpsControllerManager.getOrCreate("svc", invocation); Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); Mockito.when(invocation.getSchemaId()).thenReturn("schema.opr2"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr2.tail"); qpsStrategy = qpsControllerManager.getOrCreate("svc", invocation); Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); } private void testGetOrCreateCommon(boolean isProvider, QpsControllerManager testQpsControllerManager, Invocation invocation, OperationMeta operationMeta) { Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); setConfigWithDefaultPrefix(isProvider, "pojo.server", 200); QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(200, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server2.test"); qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("serve.test"); qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); // pojo.server.test Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); setConfigWithDefaultPrefix(isProvider, "pojo.server.test", 300); qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo.server.test", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(300, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test2"); qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(200, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.tes"); qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); Assertions.assertEquals("pojo.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); Assertions.assertEquals(200, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); } /** * Init testQpsControllerManager to test search function. */ private void initTestQpsControllerManager(boolean isProvider, QpsControllerManager testQpsControllerManager, Invocation invocation, OperationMeta operationMeta) { // pojo.server.test Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); // pojo.server.test2 Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test2"); testQpsControllerManager.getOrCreate("pojo", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); // pojo.server.tes Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.tes"); testQpsControllerManager.getOrCreate("pojo", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); // pojo.server2.test Mockito.when(invocation.getSchemaId()).thenReturn("server2"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server2.test"); testQpsControllerManager.getOrCreate("pojo", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); // pojo.serve.test Mockito.when(invocation.getSchemaId()).thenReturn("serve"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("serve.test"); testQpsControllerManager.getOrCreate("pojo", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); // pojo2.server.test Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); // poj.server.test Mockito.when(invocation.getSchemaId()).thenReturn("server"); Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); if (isProvider) { Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } else { Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); } Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); } @Test public void testMock() { Invocation invocation = getMockInvocation("service", "schema", "oper"); OperationMeta operationMeta = invocation.getOperationMeta(); SchemaMeta schemaMeta = operationMeta.getSchemaMeta(); Assertions.assertEquals("service", operationMeta.getMicroserviceName()); Assertions.assertEquals("service.schema.oper", operationMeta.getMicroserviceQualifiedName()); Assertions.assertEquals("schema.oper", operationMeta.getSchemaQualifiedName()); Assertions.assertEquals("schema", schemaMeta.getSchemaId()); } public static Invocation getMockInvocation(String microserviceName, String schemaId, String operationId) { return getMockInvocation( getMockOperationMeta(microserviceName, schemaId, operationId)); } private static Invocation getMockInvocation(OperationMeta mockOperationMeta) { Invocation invocation = Mockito.mock(Invocation.class); Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta); return invocation; } public static OperationMeta getMockOperationMeta(String microserviceName, String schemaId, String operationId) { OperationMeta operationMeta = Mockito.mock(OperationMeta.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(operationMeta.getSchemaQualifiedName()) .thenReturn(schemaId + QpsControllerManager.SEPARATOR + operationId); Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn( microserviceName + QpsControllerManager.SEPARATOR + schemaId + QpsControllerManager.SEPARATOR + operationId); Mockito.when(operationMeta.getMicroserviceName()).thenReturn(microserviceName); Mockito.when(schemaMeta.getSchemaId()).thenReturn(schemaId); return operationMeta; } private static void setConfigWithDefaultPrefix(boolean isProvider, String key, int value) { String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key; if (isProvider) { configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key; } Mockito.when(environment.getProperty(configKey, Long.class)).thenReturn(Long.valueOf(value)); HashMap updated = new HashMap<>(); updated.put(configKey, value); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); } private static void deleteConfigWithDefaultPrefix(boolean isProvider, String key) { String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key; if (isProvider) { configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key; } Mockito.when(environment.getProperty(configKey, Long.class)).thenReturn(null); HashMap updated = new HashMap<>(); updated.put(configKey, null); EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); } @Test public void testDeleteQpsController() { final String microserviceName = "springmvcClient"; final String schemaId = "controller"; final String operationId = "add"; final String configKey = "springmvcClient.controller.add"; QpsControllerManager testManager = new QpsControllerManager(true, environment); Invocation testInvocation = getMockInvocation(microserviceName, schemaId, operationId); Mockito.when(testInvocation.getSchemaId()).thenReturn(schemaId); QpsStrategy strategy1 = testManager.getOrCreate(microserviceName, testInvocation); setConfigWithDefaultPrefix(true, configKey, 1); deleteConfigWithDefaultPrefix(true, configKey); QpsStrategy strategy2 = testManager.getOrCreate(microserviceName, testInvocation); Assertions.assertEquals(((AbstractQpsStrategy) strategy1).getQpsLimit(), ((AbstractQpsStrategy) strategy2).getQpsLimit()); Assertions.assertEquals(((AbstractQpsStrategy) strategy1).getQpsLimit(), Long.valueOf(Integer.MAX_VALUE)); } } ================================================ FILE: handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.qps; import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; import org.apache.servicecomb.qps.strategy.FixedWindowStrategy; import org.apache.servicecomb.qps.strategy.LeakyBucketStrategy; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * @author GuoYl123 * @since 2020/7/16 **/ public class TestQpsStrategy { @Test public void testFixedWindowStrategy() { AbstractQpsStrategy qpsStrategy = new FixedWindowStrategy(); qpsStrategy.setKey("abc"); qpsStrategy.setQpsLimit(100L); Assertions.assertFalse(qpsStrategy.isLimitNewRequest()); qpsStrategy.setQpsLimit(1L); Assertions.assertTrue(qpsStrategy.isLimitNewRequest()); } @Test public void testLeakyBucketStrategy() { LeakyBucketStrategy qpsStrategy = new LeakyBucketStrategy(); qpsStrategy.setKey("abc"); qpsStrategy.setQpsLimit(100L); Assertions.assertFalse(qpsStrategy.isLimitNewRequest()); qpsStrategy.setQpsLimit(1L); qpsStrategy.setBucketLimit(1L); Assertions.assertTrue(qpsStrategy.isLimitNewRequest()); } } ================================================ FILE: handlers/handler-governance/README.md ================================================ Enable governance features by handler. Governance can be enabled in any different process layer, enable it in handler is most portable way. ================================================ FILE: handlers/handler-governance/pom.xml ================================================ 4.0.0 org.apache.servicecomb handlers 3.4.0-SNAPSHOT handler-governance Java Chassis::Handlers::Governance UTF-8 org.apache.servicecomb java-chassis-core org.apache.servicecomb servicecomb-governance org.apache.servicecomb registry-local test org.apache.servicecomb foundation-test-scaffolding test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test ================================================ FILE: handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.handler.governance; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadFullException; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; import jakarta.ws.rs.core.Response.Status; public class ConsumerInstanceBulkheadFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceBulkheadFilter.class); private final InstanceBulkheadHandler instanceBulkheadHandler; @Autowired public ConsumerInstanceBulkheadFilter(InstanceBulkheadHandler instanceBulkheadHandler) { this.instanceBulkheadHandler = instanceBulkheadHandler; } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1060; } @Override public String getName() { return "instance-bulkhead"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (invocation.getEndpoint() == null) { return CompletableFuture.failedFuture(new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData("instance bulkhead should work after load balancer."))); } Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); addBulkhead(dcs, request); CompletableFuture future = new CompletableFuture<>(); dcs.get().whenComplete((r, e) -> { if (e == null) { future.complete(r); return; } if (e instanceof BulkheadFullException) { // return 503 so that consumer can retry future.completeExceptionally(new InvocationException(503, "bulkhead is full and does not permit further calls.", new CommonExceptionData("bulkhead is full and does not permit further calls."))); LOGGER.warn("bulkhead is full and does not permit further calls by policy : {}", e.getMessage()); } else { future.completeExceptionally(e); } }); return future; } private void addBulkhead(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { Bulkhead bulkhead = instanceBulkheadHandler.getActuator(request); if (bulkhead != null) { dcs.withBulkhead(bulkhead); } } private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, FilterNode nextNode) { return () -> nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.handler.governance; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import io.github.resilience4j.circuitbreaker.CallNotPermittedException; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; import jakarta.ws.rs.core.Response.Status; public class ConsumerInstanceIsolationFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceIsolationFilter.class); private final InstanceIsolationHandler instanceIsolationHandler; private DiscoveryManager discoveryManager; @Autowired public ConsumerInstanceIsolationFilter(InstanceIsolationHandler instanceIsolationHandler) { this.instanceIsolationHandler = instanceIsolationHandler; } @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1050; } @Override public String getName() { return "instance-isolation"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { if (invocation.getEndpoint() == null) { return CompletableFuture.failedFuture(new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData("instance isolation should work after load balancer."))); } GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); CircuitBreakerPolicy circuitBreakerPolicy = instanceIsolationHandler.matchPolicy(request); if (circuitBreakerPolicy != null && circuitBreakerPolicy.isForceOpen()) { return CompletableFuture.failedFuture(new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("Policy \" + circuitBreakerPolicy.getName() + \" forced open and deny requests\""))); } Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); addCircuitBreaker(dcs, request); CompletableFuture future = new CompletableFuture<>(); dcs.get().whenComplete((r, e) -> { if (e == null) { future.complete(r); return; } if (e instanceof CallNotPermittedException) { LOGGER.warn("instance isolation circuitBreaker is open by policy : {}", e.getMessage()); discoveryManager.onInstanceIsolated(invocation.getEndpoint().getMicroserviceInstance(), Duration.parse(circuitBreakerPolicy.getWaitDurationInOpenState()).toMillis()); // return 503 so that consumer can retry future.complete(Response.failResp(new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("instance isolation circuitBreaker is open.")))); } else { future.completeExceptionally(e); } }); return future; } private void addCircuitBreaker(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { CircuitBreaker circuitBreaker = instanceIsolationHandler.getActuator(request); if (circuitBreaker != null) { dcs.withCircuitBreaker(circuitBreaker); } } private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, FilterNode nextNode) { return () -> nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.handler.governance; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnProperty(value = HandlerGovernanceConfiguration.GOVERNANCE_ENABLED, havingValue = "true", matchIfMissing = true) public class HandlerGovernanceConfiguration { public static final String GOVERNANCE_PREFIX = "servicecomb.governance"; public static final String GOVERNANCE_ENABLED = GOVERNANCE_PREFIX + ".enabled"; @Bean public ConsumerInstanceBulkheadFilter scbConsumerInstanceBulkheadFilter( InstanceBulkheadHandler instanceBulkheadHandler) { return new ConsumerInstanceBulkheadFilter(instanceBulkheadHandler); } @Bean public ConsumerInstanceIsolationFilter scbConsumerInstanceIsolationFilter( InstanceIsolationHandler instanceIsolationHandler) { return new ConsumerInstanceIsolationFilter(instanceIsolationHandler); } @Bean public ProviderBulkheadFilter scbProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { return new ProviderBulkheadFilter(bulkheadHandler); } @Bean public ProviderCircuitBreakerFilter scbProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { return new ProviderCircuitBreakerFilter(circuitBreakerHandler); } @Bean public ProviderRateLimitingFilter scbProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { return new ProviderRateLimitingFilter(rateLimitingHandler); } } ================================================ FILE: handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.handler.governance; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadFullException; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; public class ProviderBulkheadFilter extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderBulkheadFilter.class); private final BulkheadHandler bulkheadHandler; @Autowired public ProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { this.bulkheadHandler = bulkheadHandler; } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1880; } @Override public String getName() { return "provider-bulkhead"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); addBulkhead(dcs, request); CompletableFuture future = new CompletableFuture<>(); dcs.get().whenComplete((r, e) -> { if (e == null) { future.complete(r); return; } if (e instanceof BulkheadFullException) { future.completeExceptionally( new InvocationException(429, "bulkhead is full and does not permit further calls.", new CommonExceptionData("bulkhead is full and does not permit further calls."))); LOGGER.warn("bulkhead is full and does not permit further calls by policy : {}", e.getMessage()); } else { future.completeExceptionally(e); } }); return future; } private void addBulkhead(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { Bulkhead bulkhead = bulkheadHandler.getActuator(request); if (bulkhead != null) { dcs.withBulkhead(bulkhead); } } private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, FilterNode nextNode) { return () -> nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.handler.governance; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import io.github.resilience4j.circuitbreaker.CallNotPermittedException; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; public class ProviderCircuitBreakerFilter extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderCircuitBreakerFilter.class); private final CircuitBreakerHandler circuitBreakerHandler; @Autowired public ProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { this.circuitBreakerHandler = circuitBreakerHandler; } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1890; } @Override public String getName() { return "provider-circuit-breaker"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); addCircuitBreaker(dcs, request); CompletableFuture future = new CompletableFuture<>(); dcs.get().whenComplete((r, e) -> { if (e == null) { future.complete(r); return; } if (e instanceof CallNotPermittedException) { future.completeExceptionally(new InvocationException(429, "circuitBreaker is open.", new CommonExceptionData("circuitBreaker is open."))); LOGGER.warn("circuitBreaker is open by policy : {}", e.getMessage()); } else { future.completeExceptionally(e); } }); return future; } private void addCircuitBreaker(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { CircuitBreaker circuitBreaker = circuitBreakerHandler.getActuator(request); if (circuitBreaker != null) { dcs.withCircuitBreaker(circuitBreaker); } } private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, FilterNode nextNode) { return () -> nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.handler.governance; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RequestNotPermitted; public class ProviderRateLimitingFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderRateLimitingFilter.class); private final RateLimitingHandler rateLimitingHandler; @Autowired public ProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { this.rateLimitingHandler = rateLimitingHandler; } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1900; } @Override public String getName() { return "rate-limiting"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); addRateLimiting(dcs, request); CompletableFuture future = new CompletableFuture<>(); dcs.get().whenComplete((r, e) -> { if (e == null) { future.complete(r); return; } if (e instanceof RequestNotPermitted) { future.completeExceptionally( new InvocationException(429, "rate limited.", new CommonExceptionData("rate limited."))); LOGGER.warn("the request is rate limit by policy : {}", e.getMessage()); } else { future.completeExceptionally(e); } }); return future; } private void addRateLimiting(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); if (rateLimiter != null) { dcs.withRateLimiter(rateLimiter); } } private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, FilterNode nextNode) { return () -> nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-governance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.handler.governance.HandlerGovernanceConfiguration ================================================ FILE: handlers/handler-loadbalance/pom.xml ================================================ 4.0.0 org.apache.servicecomb handlers 3.4.0-SNAPSHOT handler-loadbalance Java Chassis::Handlers::Loadbalance UTF-8 org.apache.servicecomb java-chassis-core org.apache.commons commons-lang3 io.github.resilience4j resilience4j-all org.apache.servicecomb registry-local test org.apache.servicecomb foundation-test-scaffolding test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test ================================================ FILE: handlers/handler-loadbalance/readme.MD ================================================ 参考: https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers LoadBalancer Rule Ping ServerList ServerListFilter ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.Map; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; /** * configuration items */ public final class Configuration { //// 2.1 configuration items public static final String ROOT = "servicecomb.loadbalance."; public static final String RULE_STRATEGY_GLOBAL = "servicecomb.loadbalance.strategy.name"; public static final String RULE_STRATEGY_NAME = "strategy.name"; // 2.0 configuration items public static final String ROOT_20 = "ribbon."; // SessionStickinessRule configuration public static final String SESSION_TIMEOUT_IN_SECONDS = "SessionStickinessRule.sessionTimeoutInSeconds"; public static final String SUCCESSIVE_FAILED_TIMES = "SessionStickinessRule.successiveFailedTimes"; public static final String FILTER_ISOLATION = "isolation."; public static final String FILTER_MAX_SINGLE_TEST_WINDOW = "maxSingleTestWindow"; public static final String TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN = "servicecomb.loadbalance.%s.transactionControl.options"; public static final Configuration INSTANCE = new Configuration(); public record RuleType(int type, String value) { public static final int TYPE_SCHEMA = 1; public static final int TYPE_OPERATION = 2; public String getValue() { return value; } public int getType() { return type; } } private Configuration() { } public RuleType getRuleStrategyName(Invocation invocation) { String value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + invocation.getSchemaId() + "." + invocation.getOperationName() + "." + RULE_STRATEGY_NAME); if (value != null) { return new RuleType(RuleType.TYPE_OPERATION, value); } value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + invocation.getSchemaId() + "." + RULE_STRATEGY_NAME); if (value != null) { return new RuleType(RuleType.TYPE_SCHEMA, value); } value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + RULE_STRATEGY_NAME); if (value != null) { return new RuleType(RuleType.TYPE_SCHEMA, value); } return new RuleType(RuleType.TYPE_SCHEMA, getStringProperty("RoundRobin", RULE_STRATEGY_GLOBAL)); } public int getSessionTimeoutInSeconds(String microservice) { final int defaultValue = 30; String p = getStringProperty("30", ROOT + microservice + "." + SESSION_TIMEOUT_IN_SECONDS, ROOT + SESSION_TIMEOUT_IN_SECONDS); try { return Integer.parseInt(p); // can be negative } catch (NumberFormatException e) { return defaultValue; } } public int getSuccessiveFailedTimes(String microservice) { final int defaultValue = 5; String p = getStringProperty("5", ROOT + microservice + "." + SUCCESSIVE_FAILED_TIMES, ROOT + SUCCESSIVE_FAILED_TIMES); try { return Integer.parseInt(p); // can be negative } catch (NumberFormatException e) { return defaultValue; } } public int getMaxSingleTestWindow() { final int defaultValue = 60000; String p = getStringProperty(Integer.toString(defaultValue), ROOT + FILTER_ISOLATION + FILTER_MAX_SINGLE_TEST_WINDOW); try { int result = Integer.parseInt(p); if (result >= 0) { return result; } return defaultValue; } catch (NumberFormatException e) { return defaultValue; } } public Map getFlowsplitFilterOptions(String microservice) { String keyPrefix = String.format(TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN, microservice); return ConfigUtil.stringPropertiesWithPrefix(LegacyPropertyFactory.getEnvironment(), keyPrefix); } public static String getStringProperty(String defaultValue, String... keys) { String property; for (String key : keys) { property = LegacyPropertyFactory.getStringProperty(key); if (property != null) { return property; } } return defaultValue; } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; /** * By implements ExtensionsFactory, users can add new extends for rules, filters, etc. */ public interface ExtensionsFactory { boolean isSupport(String key, String value); default RuleExt createLoadBalancerRule(String ruleName) { return null; } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; public class ExtensionsManager { private final List extensionsFactories; public ExtensionsManager(List extensionsFactories) { this.extensionsFactories = extensionsFactories; } public RuleExt createLoadBalancerRule(String ruleStrategyName) { RuleExt rule = null; for (ExtensionsFactory factory : extensionsFactories) { if (factory.isSupport(Configuration.RULE_STRATEGY_NAME, ruleStrategyName)) { rule = factory.createLoadBalancerRule(ruleStrategyName); break; } } if (rule == null) { rule = new RoundRobinRuleExt(); } return rule; } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.loadbalance.filter.InstancePropertyDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.PriorityInstancePropertyDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.ZoneAwareDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnProperty(value = LoadBalanceConfiguration.LOAD_BALANCE_ENABLED, havingValue = "true", matchIfMissing = true) public class LoadBalanceConfiguration { public static final String LOAD_BALANCE_PREFIX = "servicecomb.loadbalance"; public static final String LOAD_BALANCE_ENABLED = LOAD_BALANCE_PREFIX + ".enabled"; @Bean public LoadBalanceFilter scbLoadBalanceFilter(ExtensionsManager extensionsManager, DiscoveryTree discoveryTree, SCBEngine scbEngine) { return new LoadBalanceFilter(extensionsManager, discoveryTree, scbEngine); } @Bean public RuleNameExtensionsFactory scbRuleNameExtensionsFactory() { return new RuleNameExtensionsFactory(); } @Bean public ExtensionsManager scbExtensionsManager(List extensionsFactories) { return new ExtensionsManager(extensionsFactories); } @Bean public PriorityInstancePropertyDiscoveryFilter scbPriorityInstancePropertyDiscoveryFilter() { return new PriorityInstancePropertyDiscoveryFilter(); } @Bean public InstancePropertyDiscoveryFilter scbInstancePropertyDiscoveryFilter() { return new InstancePropertyDiscoveryFilter(); } @Bean public ServerDiscoveryFilter scbServerDiscoveryFilter() { return new ServerDiscoveryFilter(); } @Bean public ZoneAwareDiscoveryFilter scbZoneAwareDiscoveryFilter() { return new ZoneAwareDiscoveryFilter(); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.net.URI; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.RetryContext; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.loadbalance.Configuration.RuleType; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; import io.github.resilience4j.core.metrics.Metrics.Outcome; import jakarta.ws.rs.core.Response.Status; public class LoadBalanceFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String CONTEXT_KEY_LAST_SERVER = "x-context-last-server"; // Enough times to make sure to choose a different server in high volume. private static final int COUNT = 17; public static final String CONTEXT_KEY_SERVER_LIST = "x-context-server-list"; public static final String SERVICECOMB_SERVER_ENDPOINT = "scb-endpoint"; private static final Logger LOGGER = LoggerFactory.getLogger(LoadBalanceFilter.class); private final DiscoveryTree discoveryTree; // key为grouping filter qualified name private final Map loadBalancerMap = new ConcurrentHashMapEx<>(); private final Object lock = new Object(); private final ExtensionsManager extensionsManager; private final SCBEngine scbEngine; // set endpoint in invocation.localContext // ignore logic of loadBalance public boolean supportDefinedEndpoint; @Autowired public LoadBalanceFilter(ExtensionsManager extensionsManager, DiscoveryTree discoveryTree, SCBEngine scbEngine) { preCheck(scbEngine); this.scbEngine = scbEngine; this.supportDefinedEndpoint = this.scbEngine.getEnvironment() .getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", boolean.class, false); this.extensionsManager = extensionsManager; this.discoveryTree = discoveryTree; EventManager.register(this); } @Subscribe @SuppressWarnings("unused") public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { Set changedKeys = event.getChanged(); for (String key : changedKeys) { if (key.startsWith(Configuration.ROOT)) { synchronized (lock) { clearLoadBalancer(); } LOGGER.info("clear load balance rule for configuration changed, {}", key); break; } } } private void preCheck(SCBEngine scbEngine) { // Old configurations check.Just print an error, because configurations may given in dynamic and fail on runtime. String policyName = scbEngine.getEnvironment() .getProperty("servicecomb.loadbalance.NFLoadBalancerRuleClassName"); if (!StringUtils.isEmpty(policyName)) { LOGGER.error("[servicecomb.loadbalance.NFLoadBalancerRuleClassName] is not supported anymore." + "use [servicecomb.loadbalance.strategy.name] instead."); } String filterNames = scbEngine.getEnvironment() .getProperty("servicecomb.loadbalance.serverListFilters"); if (!StringUtils.isEmpty(filterNames)) { LOGGER.error( "Server list implementation changed to SPI. Configuration [servicecomb.loadbalance.serverListFilters]" + " is not used any more. For ServiceComb defined filters, you do not need config and can " + "remove this configuration safely. If you define your own filter, need to change it to SPI to make it work."); } } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER; } @Override public String getName() { return "load-balance"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { try { if (handleSuppliedEndpoint(invocation)) { invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, false); return nextNode.onFilter(invocation); } } catch (Exception e) { return CompletableFuture.failedFuture(e); } invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, true); LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation); return send(invocation, nextNode, loadBalancer); } // user's can invoke a service by supplying target Endpoint. // in this case, do not use load balancer, and no stats of server calculated, no retrying. private boolean handleSuppliedEndpoint(Invocation invocation) throws Exception { if (invocation.isEdge()) { return false; } if (invocation.getEndpoint() != null) { return true; } if (supportDefinedEndpoint) { return defineEndpointAndHandle(invocation); } return false; } private Endpoint parseEndpoint(String endpointUri) throws Exception { URI formatUri = new URI(endpointUri); Transport transport = scbEngine.getTransportManager().findTransport(formatUri.getScheme()); if (transport == null) { LOGGER.error("not deployed transport {}, ignore {}.", formatUri.getScheme(), endpointUri); throw new InvocationException(Status.BAD_REQUEST, "the endpoint's transport is not found."); } return new Endpoint(transport, endpointUri); } private boolean defineEndpointAndHandle(Invocation invocation) throws Exception { Object endpoint = invocation.getLocalContext(SERVICECOMB_SERVER_ENDPOINT); if (endpoint == null) { return false; } if (endpoint instanceof String) { // compatible to old usage endpoint = parseEndpoint((String) endpoint); } invocation.setEndpoint((Endpoint) endpoint); return true; } private void clearLoadBalancer() { loadBalancerMap.clear(); } @VisibleForTesting CompletableFuture send(Invocation invocation, FilterNode filterNode, LoadBalancer chosenLB) { long time = System.currentTimeMillis(); ServiceCombServer server = chooseServer(invocation, chosenLB); if (null == server) { return CompletableFuture.failedFuture( new InvocationException(Status.INTERNAL_SERVER_ERROR, String.format("No available address found for %s/%s.", invocation.getAppId(), invocation.getMicroserviceName()))); } invocation.setEndpoint(server.getEndpoint()); return filterNode.onFilter(invocation).whenComplete((r, e) -> { if (e != null || isFailedResponse(r)) { server.getServerMetrics().record((System.currentTimeMillis() - time), TimeUnit.MILLISECONDS, Outcome.ERROR); } else { server.getServerMetrics().record((System.currentTimeMillis() - time), TimeUnit.MILLISECONDS, Outcome.SUCCESS); } }); } private ServiceCombServer chooseServer(Invocation invocation, LoadBalancer chosenLB) { RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); if (retryContext == null) { return chosenLB.chooseServer(invocation); } if (!retryContext.isRetry()) { ServiceCombServer server = chosenLB.chooseServer(invocation); invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, server); return server; } ServiceCombServer lastServer = invocation.getLocalContext(CONTEXT_KEY_LAST_SERVER); ServiceCombServer nextServer = lastServer; if (!retryContext.trySameServer()) { for (int i = 0; i < COUNT; i++) { ServiceCombServer s = chosenLB.chooseServer(invocation); if (s == null) { break; } if (!s.equals(nextServer)) { nextServer = s; break; } } } LOGGER.warn("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", invocation.getMicroserviceQualifiedName(), nextServer == null ? "" : nextServer.getEndpoint().getEndpoint(), lastServer == null ? "" : nextServer.getEndpoint().getEndpoint(), invocation.getTraceId()); invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, nextServer); return nextServer; } protected boolean isFailedResponse(Response resp) { if (resp.isFailed()) { if (resp.getResult() instanceof InvocationException) { InvocationException e = resp.getResult(); return e.getStatusCode() == ExceptionFactory.CONSUMER_INNER_STATUS_CODE || e.getStatusCode() == Status.SERVICE_UNAVAILABLE.getStatusCode() || e.getStatusCode() == Status.REQUEST_TIMEOUT.getStatusCode(); } else { return true; } } else { return false; } } protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation) { DiscoveryContext context = new DiscoveryContext(); context.setInputParameters(invocation); VersionedCache serversVersionedCache = discoveryTree.discovery(context, invocation.getAppId(), invocation.getMicroserviceName()); invocation.addLocalContext(CONTEXT_KEY_SERVER_LIST, serversVersionedCache.data()); RuleType ruleType = Configuration.INSTANCE.getRuleStrategyName(invocation); String cacheKey; if (ruleType.getType() == RuleType.TYPE_SCHEMA) { cacheKey = invocation.getAppId() + "-" + invocation.getMicroserviceName() + "-" + invocation.getSchemaId(); } else { cacheKey = invocation.getAppId() + "-" + invocation.getMicroserviceName() + "-" + invocation.getSchemaId() + "-" + invocation.getOperationName(); } return loadBalancerMap.computeIfAbsent(cacheKey, key -> createLoadBalancer(ruleType, key, invocation.getMicroserviceName())); } private LoadBalancer createLoadBalancer(RuleType ruleType, String cacheKey, String microserviceName) { RuleExt rule = extensionsManager.createLoadBalancerRule(ruleType.getValue()); LOGGER.info("Using load balance rule {} for microservice {}.", rule.getClass().getName(), cacheKey); return new LoadBalancer(rule, microserviceName); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; /** * A load balancer with RuleExt and ServerListFilterExt */ public class LoadBalancer { private static final Logger LOGGER = LoggerFactory.getLogger(LoadBalancer.class); private static final AtomicInteger id = new AtomicInteger(0); private final RuleExt rule; private final String microServiceName; private List filters; public LoadBalancer(RuleExt rule, String microServiceName) { this.microServiceName = microServiceName; this.rule = rule; // load new instances, because filters work on service information this.filters = SPIServiceUtils.loadSortedService(ServerListFilterExt.class); this.rule.setLoadBalancer(this); this.filters.forEach((item) -> item.setLoadBalancer(this)); } public ServiceCombServer chooseServer(Invocation invocation) { List servers = invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); int serversCount = servers.size(); for (ServerListFilterExt filterExt : filters) { if (!filterExt.enabled()) { continue; } servers = filterExt.getFilteredListOfServers(servers, invocation); if (servers.isEmpty() && serversCount > 0) { LOGGER.warn("There are not servers exist after filtered by {}.", filterExt.getClass()); break; } } ServiceCombServer server = rule.choose(servers, invocation); if (null == server) { return null; } return server; } public String getMicroServiceName() { return microServiceName; } @VisibleForTesting void setFilters(List filters) { this.filters = filters; } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import org.apache.servicecomb.core.Invocation; /** * A random rule. */ public class RandomRuleExt implements RuleExt { @Override public ServiceCombServer choose(List servers, Invocation invocation) { if (servers.isEmpty()) { return null; } int index = ThreadLocalRandom.current().nextInt(servers.size()); return servers.get(index); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RoundRobinRuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; /** * A round robin rule */ public class RoundRobinRuleExt implements RuleExt { private final AtomicInteger counter = new AtomicInteger(0); @Override public ServiceCombServer choose(List servers, Invocation invocation) { if (servers.isEmpty()) { return null; } int index = Math.abs(counter.getAndIncrement()) % servers.size(); return servers.get(index); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import org.apache.servicecomb.core.Invocation; /** * Load balance rule to support invocation based filters. */ public interface RuleExt { default void setLoadBalancer(LoadBalancer loadBalancer) { } ServiceCombServer choose(List servers, Invocation invocation); } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.Collection; import com.google.common.collect.Lists; public class RuleNameExtensionsFactory implements ExtensionsFactory { private static final Collection ACCEPT_KEYS = Lists.newArrayList( Configuration.RULE_STRATEGY_NAME); private static final String RULE_RoundRobin = "RoundRobin"; private static final String RULE_Random = "Random"; private static final String RULE_WeightedResponse = "WeightedResponse"; private static final String RULE_SessionStickiness = "SessionStickiness"; private static final Collection ACCEPT_VALUES = Lists.newArrayList( RULE_RoundRobin, RULE_Random, RULE_WeightedResponse, RULE_SessionStickiness); @Override public boolean isSupport(String key, String value) { return ACCEPT_KEYS.contains(key) && ACCEPT_VALUES.contains(value); } @Override public RuleExt createLoadBalancerRule(String ruleName) { if (RULE_RoundRobin.equals(ruleName)) { return new RoundRobinRuleExt(); } else if (RULE_Random.equals(ruleName)) { return new RandomRuleExt(); } else if (RULE_WeightedResponse.equals(ruleName)) { return new WeightedResponseTimeRuleExt(); } else if (RULE_SessionStickiness.equals(ruleName)) { return new SessionStickinessRule(); } else { throw new IllegalStateException("unexpected code to reach here, value is " + ruleName); } } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import org.apache.servicecomb.core.Invocation; /** * Base interface for server list filters. * * Robin ServerListFilter can not support invocation based filter strategies, so we create a new one to * support this. */ public interface ServerListFilterExt { int ORDER_NORMAL = 0; default int getOrder() { return ORDER_NORMAL; } default boolean enabled() { return true; } default void setLoadBalancer(LoadBalancer loadBalancer) { } List getFilteredListOfServers(List servers, Invocation invocation); } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.concurrent.TimeUnit; import io.github.resilience4j.core.metrics.FixedSizeSlidingWindowMetrics; import io.github.resilience4j.core.metrics.Metrics.Outcome; import io.github.resilience4j.core.metrics.Snapshot; /** * ServiceCombServer states */ public class ServerMetrics { private final FixedSizeSlidingWindowMetrics metrics = new FixedSizeSlidingWindowMetrics(50); public Snapshot record(long duration, TimeUnit durationUnit, Outcome outcome) { return metrics.record(duration, durationUnit, outcome); } public Snapshot getSnapshot() { return metrics.getSnapshot(); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.registry.api.DiscoveryInstance; /** * Server object used for transports and load balancer. * */ public class ServiceCombServer { private final Endpoint endpoint; private final String microserviceName; private final ServerMetrics serverMetrics; public ServiceCombServer(String microserviceName, Endpoint endpoint) { this.microserviceName = microserviceName; this.endpoint = endpoint; this.serverMetrics = new ServerMetrics(); } public String getMicroserviceName() { return this.microserviceName; } public Endpoint getEndpoint() { return endpoint; } public DiscoveryInstance getInstance() { return endpoint.getMicroserviceInstance(); } @Override public String toString() { return endpoint.getEndpoint(); } public String getHost() { return endpoint.getEndpoint(); } public ServerMetrics getServerMetrics() { return serverMetrics; } @Override public boolean equals(Object o) { if (o instanceof ServiceCombServer) { return this.getInstance().getInstanceId() .equals(((ServiceCombServer) o).getInstance().getInstanceId()) && StringUtils.equals(endpoint.getEndpoint(), ((ServiceCombServer) o).getEndpoint().getEndpoint()); } else { return false; } } @Override public int hashCode() { return Objects.hash(this.getInstance().getInstanceId(), this.endpoint); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.List; import org.apache.servicecomb.core.Invocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; /** * 会话保持策略:优先选择上一次选中的服务器,保证请求都发送到同一个服务器上去。 * 提供当会话过期或者失败次数超过限制后,轮询选择其他服务器的能力。 * */ public class SessionStickinessRule implements RuleExt { private static final Logger LOG = LoggerFactory.getLogger(SessionStickinessRule.class); private final Object lock = new Object(); // use random rule as the trigger rule, to prevent consumer instance select the same producer instance. private final RuleExt triggerRule; private volatile ServiceCombServer lastServer = null; private volatile long lastAccessedTime = 0; private volatile boolean errorThresholdMet = false; private static final int MILLI_COUNT_IN_SECOND = 1000; private String microserviceName; public SessionStickinessRule() { triggerRule = new RoundRobinRuleExt(); } @Override public void setLoadBalancer(LoadBalancer loadBalancer) { this.microserviceName = loadBalancer.getMicroServiceName(); } private ServiceCombServer chooseNextServer(List servers, Invocation invocation) { lastServer = triggerRule.choose(servers, invocation); lastAccessedTime = System.currentTimeMillis(); return lastServer; } private ServiceCombServer chooseInitialServer(List servers, Invocation invocation) { synchronized (lock) { if (lastServer == null) { chooseNextServer(servers, invocation); } } return lastServer; } @VisibleForTesting ServiceCombServer chooseServerWhenTimeout(List servers, Invocation invocation) { synchronized (lock) { if (isTimeOut()) { chooseNextServer(servers, invocation); } } return lastServer; } private ServiceCombServer chooseServerErrorThresholdMet(List servers, Invocation invocation) { synchronized (lock) { if (errorThresholdMet) { chooseNextServer(servers, invocation); errorThresholdMet = false; } } return lastServer; } private boolean isTimeOut() { return Configuration.INSTANCE.getSessionTimeoutInSeconds(microserviceName) > 0 && System.currentTimeMillis() - this.lastAccessedTime > ((long) Configuration.INSTANCE.getSessionTimeoutInSeconds(microserviceName) * MILLI_COUNT_IN_SECOND); } private boolean isErrorThresholdMet(ServiceCombServer server) { int successiveFailedCount = server.getServerMetrics().getSnapshot().getNumberOfFailedCalls(); return Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName) > 0 && successiveFailedCount >= Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName); } @Override public ServiceCombServer choose(List servers, Invocation invocation) { if (lastServer == null) { return chooseInitialServer(servers, invocation); } if (isTimeOut()) { LOG.warn("session timeout. choose another server."); return chooseServerWhenTimeout(servers, invocation); } else { this.lastAccessedTime = System.currentTimeMillis(); } if (isErrorThresholdMet(lastServer)) { LOG.warn("reached max error. choose another server."); errorThresholdMet = true; return chooseServerErrorThresholdMet(servers, invocation); } if (!servers.contains(lastServer)) { return chooseNextServer(servers, invocation); } return lastServer; } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; /** * Rule based on response time. */ public class WeightedResponseTimeRuleExt extends RoundRobinRuleExt { // when all servers are very fast(less than MIN_GAP), use round-robin rule. private static final double MIN_GAP = 10d; // calculate stats once per RANDOM_PERCENT requests. private static final int RANDOM_PERCENT = 1000; private final Object lock = new Object(); private final AtomicInteger counter = new AtomicInteger(0); // notices: rule will always use as a fixed group of instance, see LoadBalancer for details. private volatile int size = -1; private volatile List cacheStates = new ArrayList<>(); @Override public ServiceCombServer choose(List servers, Invocation invocation) { int count = counter.getAndIncrement(); if (count % RANDOM_PERCENT == 0 || size != servers.size()) { synchronized (lock) { this.cacheStates = doCalculateTotalWeights(servers); this.size = servers.size(); } } List stats = this.cacheStates; if (stats.size() > 0) { double finalTotal = stats.get(stats.size() - 1); List weights = new ArrayList<>(servers.size()); for (int i = 0; i < stats.size() - 1; i++) { weights.add(finalTotal - stats.get(i)); } double ran = ThreadLocalRandom.current().nextDouble() * finalTotal * (servers.size() - 1); for (int i = 0; i < weights.size(); i++) { ran -= weights.get(i); if (ran < 0) { return servers.get(i); } } return servers.get(servers.size() - 1); } return super.choose(servers, invocation); } private static List doCalculateTotalWeights(List servers) { List stats = new ArrayList<>(servers.size() + 1); double totalWeights = 0; boolean needRandom = false; for (ServiceCombServer server : servers) { // this method will create new instance, so we cache the states. double avgTime = server.getServerMetrics().getSnapshot().getAverageDuration().toMillis(); if (!needRandom && avgTime > MIN_GAP) { needRandom = true; } totalWeights += avgTime; stats.add(avgTime); } stats.add(totalWeights); if (needRandom) { return stats; } else { return new ArrayList<>(); } } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.exception; import org.apache.servicecomb.core.exception.CseException; import org.apache.servicecomb.core.exception.ExceptionUtils; public class LoadbalanceExceptionUtils extends ExceptionUtils { public static final String CSE_HANDLER_LB_WRONG_RULE = "servicecomb.handler.lb.wrong.rule"; static { ERROR_DESC_MGR.register(CSE_HANDLER_LB_WRONG_RULE, "Configured rule name is wrong."); } public static CseException createLoadbalanceException(String code, Throwable cause, Object... args) { String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); return new CseException(code, msg, cause); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.loadbalance.Configuration; import org.apache.servicecomb.registry.discovery.AbstractDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; /** * Instance property based filter */ public class InstancePropertyDiscoveryFilter extends AbstractDiscoveryFilter { private static final String MATCHED = "matched"; public static final String SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED = "servicecomb.loadbalance.filter.instanceProperty.enabled"; @Override public int getOrder() { return 400; } @Override public boolean enabled() { if (enabled == null) { enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED, value -> enabled = value, true); } return enabled; } @Override public boolean isGroupingFilter() { return false; } @Override protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { List matchedInstance = new ArrayList<>(); Invocation invocation = context.getInputParameters(); List instances = parent.data(); Map filterOptions = Configuration.INSTANCE.getFlowsplitFilterOptions(invocation.getMicroserviceName()); instances.forEach((target) -> { if (allowVisit(target, filterOptions)) { matchedInstance.add(target); } }); parent.child(MATCHED, new DiscoveryTreeNode() .subName(parent, MATCHED) .data(matchedInstance)); } @Override protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { return MATCHED; } protected boolean allowVisit(StatefulDiscoveryInstance instance, Map filterOptions) { Map propertiesMap = instance.getProperties(); for (Entry entry : filterOptions.entrySet()) { if (!entry.getValue().equals(propertiesMap.get(entry.getKey()))) { return false; } } return true; } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.registry.discovery.AbstractDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import jakarta.validation.constraints.NotNull; /** * Instance property with priority filter */ public class PriorityInstancePropertyDiscoveryFilter extends AbstractDiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(PriorityInstancePropertyDiscoveryFilter.class); private static final String ALL_INSTANCE = "allInstance"; public static final String SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY = "servicecomb.loadbalance.filter.priorityInstanceProperty.key"; private String propertyKey; private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { propertyKey = dynamicProperties.getStringProperty(SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY, value -> propertyKey = value, "environment"); // group all instance by property List instances = parent.data(); Map> groupByProperty = new HashMap<>(); for (StatefulDiscoveryInstance microserviceInstance : instances) { String propertyValue = new PriorityInstanceProperty(propertyKey, microserviceInstance).getPropertyValue(); groupByProperty.computeIfAbsent(propertyValue, key -> new ArrayList<>()) .add(microserviceInstance); } Map children = new HashMap<>(); for (Map.Entry> entry : groupByProperty.entrySet()) { children.put(entry.getKey(), new DiscoveryTreeNode().subName(parent, entry.getKey()).data(entry.getValue())); } children.put(ALL_INSTANCE, new DiscoveryTreeNode().subName(parent, ALL_INSTANCE).data(instances)); parent.children(children); } @Override protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { Invocation invocation = context.getInputParameters(); // context property has precedence over instance property String initPropertyValue = invocation.getContext() .computeIfAbsent("x-" + propertyKey, key -> new PriorityInstanceProperty(propertyKey, BootStrapProperties.readServiceProperties(environment).get(propertyKey)) .getPropertyValue()); PriorityInstanceProperty currentProperty = context.getContextParameter(propertyKey); // start with initial value, then search with priority if (Objects.isNull(currentProperty)) { currentProperty = new PriorityInstanceProperty(propertyKey, initPropertyValue); while (!parent.children().containsKey(currentProperty.getPropertyValue()) && currentProperty.hasChildren()) { currentProperty = currentProperty.child(); } } else { if (currentProperty.hasChildren()) { currentProperty = currentProperty.child(); } } LOGGER.debug("Discovery instance filter by {}", currentProperty); context.putContextParameter(propertyKey, currentProperty); // stop push filter stack if property is empty if (currentProperty.isEmpty()) { return currentProperty.getPropertyValue(); } context.pushRerunFilter(); return currentProperty.getPropertyValue(); } @Override public boolean enabled() { if (enabled == null) { enabled = dynamicProperties.getBooleanProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.enabled", value -> enabled = value, false); } return enabled; } @Override public int getOrder() { return new InstancePropertyDiscoveryFilter().getOrder() + 1; } static class PriorityInstanceProperty { private static final int MAX_LENGTH = 10000; private static final String SEPARATOR = "."; private final String propertyKey; private final String propertyVal; /** * Constructor * * @param key property key * @param value property value */ public PriorityInstanceProperty(@NotNull String key, String value) { propertyKey = key; if (Objects.isNull(value)) { value = StringUtils.EMPTY; } if (value.length() > MAX_LENGTH) { throw new IllegalArgumentException("property value exceed max length"); } propertyVal = value; } /** * Constructor * * @param key property key * @param microserviceInstance instance */ public PriorityInstanceProperty(@NotNull String key, @NotNull StatefulDiscoveryInstance microserviceInstance) { this(key, Optional.ofNullable(microserviceInstance.getProperties().get(key)) .orElse(StringUtils.EMPTY)); } /** * whether property is empty * * @return result */ public boolean isEmpty() { return StringUtils.isEmpty(propertyVal); } /** * does property have lower priority children * * @return result */ public boolean hasChildren() { return StringUtils.isNotEmpty(propertyVal); } /** * get lower priority child * * @return result */ public PriorityInstanceProperty child() { if (propertyVal.contains(SEPARATOR)) { return new PriorityInstanceProperty(propertyKey, StringUtils.substringBeforeLast(propertyVal, SEPARATOR)); } return new PriorityInstanceProperty(propertyKey, StringUtils.EMPTY); } /** * get property value * * @return propertyVal */ public String getPropertyValue() { return propertyVal; } @Override public String toString() { return "PriorityInstanceProperty{key=" + propertyKey + ", value=" + propertyVal + '}'; } } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ServerDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.registry.discovery.EndpointDiscoveryFilter; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServerDiscoveryFilter extends EndpointDiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ServerDiscoveryFilter.class); public ServerDiscoveryFilter() { } @Override protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, StatefulDiscoveryInstance instance) { Transport transport = scbEngine.getTransportManager().findTransport(transportName); if (transport == null) { LOGGER.info("not deployed transport {}, ignore {}.", transportName, endpoint); return null; } Invocation invocation = context.getInputParameters(); return new ServiceCombServer(invocation.getMicroserviceName(), new Endpoint(transport, endpoint, instance)); } } ================================================ FILE: handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.registry.discovery.AbstractGroupDiscoveryFilter; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.springframework.beans.factory.annotation.Autowired; public class ZoneAwareDiscoveryFilter extends AbstractGroupDiscoveryFilter { public static final String PARAMETER = "zone_aware_level"; public static final String GROUP_PREFIX = "zone_aware_group_"; public static final String GROUP_SIZE = "zone_aware_group_size"; public static final String CONFIG_ENABLED = "servicecomb.loadbalance.filter.zoneaware.enabled"; public static final String CONFIG_RATIO = "servicecomb.loadbalance.filter.zoneaware.ratio"; public static final String CONFIG_RATIO_CEILING = "servicecomb.loadbalance.filter.zoneaware.ratioCeiling"; private DataCenterProperties dataCenterProperties; private Integer ratio; private Integer ratioCeiling; @Autowired @SuppressWarnings("unused") public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { this.dataCenterProperties = dataCenterProperties; } @Override public int getOrder() { return -9000; } @Override public boolean enabled() { if (enabled == null) { enabled = dynamicProperties.getBooleanProperty(CONFIG_ENABLED, value -> enabled = value, true); } return enabled; } private int getRatio() { if (ratio == null) { ratio = dynamicProperties.getIntProperty(CONFIG_RATIO, value -> ratio = value, 30); } return ratio; } private int getRatioCeiling(int defaultValue) { if (ratioCeiling == null) { ratioCeiling = dynamicProperties.getIntProperty(CONFIG_RATIO_CEILING, value -> ratioCeiling = value, defaultValue); } return ratioCeiling; } @Override protected String contextParameter() { return PARAMETER; } @Override protected String groupsSizeParameter() { return GROUP_SIZE; } @Override protected String groupPrefix() { return GROUP_PREFIX; } @Override public void init(DiscoveryContext context, DiscoveryTreeNode parent) { List instances = parent.data(); List instancesRegionAndAZMatch = new ArrayList<>(); List instancesAZMatch = new ArrayList<>(); List instancesNoMatch = new ArrayList<>(); int groups = 1; for (StatefulDiscoveryInstance server : instances) { if (regionAndAZMatch(server)) { instancesRegionAndAZMatch.add(server); } else if (regionMatch(server)) { instancesAZMatch.add(server); } else { instancesNoMatch.add(server); } } int ratio = getRatio(); int ratioCeiling = getRatioCeiling(100 - ratio); if (hasEnoughMembers(instances.size(), instancesRegionAndAZMatch.size(), ratio, ratioCeiling)) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(instancesRegionAndAZMatch)); groups++; } else { instancesAZMatch.addAll(instancesRegionAndAZMatch); } if (hasEnoughMembers(instances.size(), instancesAZMatch.size(), ratio, ratioCeiling)) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(instancesAZMatch)); groups++; } else { instancesNoMatch.addAll(instancesAZMatch); } parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(instancesNoMatch)); parent.attribute(GROUP_SIZE, groups); } private boolean hasEnoughMembers(int totalSize, int groupSize, int ratio, int ratioCeiling) { if (totalSize == 0 || groupSize == 0) { return false; } int actual = Math.floorDiv(groupSize * 100, totalSize); return actual >= ratio && actual <= ratioCeiling; } private boolean regionAndAZMatch(StatefulDiscoveryInstance target) { if (dataCenterProperties.getRegion() != null && dataCenterProperties.getAvailableZone() != null && target.getDataCenterInfo() != null) { return dataCenterProperties.getRegion() .equals(target.getDataCenterInfo().getRegion()) && dataCenterProperties.getAvailableZone() .equals(target.getDataCenterInfo().getAvailableZone()); } return false; } private boolean regionMatch(StatefulDiscoveryInstance target) { if (dataCenterProperties.getRegion() != null && target.getDataCenterInfo() != null) { return dataCenterProperties.getRegion() .equals(target.getDataCenterInfo().getRegion()); } return false; } } ================================================ FILE: handlers/handler-loadbalance/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.loadbalance.LoadBalanceConfiguration ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/MyServerListFilterExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.core.Invocation; /** * @author l00168639 * */ public class MyServerListFilterExt implements ServerListFilterExt { @Override public List getFilteredListOfServers(List serverList, Invocation invocation) { if (invocation.getAppId().equals("test")) { return new ArrayList<>(); } return serverList; } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; /** * */ public class TestConfiguration { Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { LegacyPropertyFactory.setEnvironment(environment); } @After public void after() { } @Test public void testConstants() { Assertions.assertEquals("servicecomb.loadbalance.", Configuration.ROOT); Assertions.assertEquals("ribbon.", Configuration.ROOT_20); Assertions.assertEquals("SessionStickinessRule.successiveFailedTimes", Configuration.SUCCESSIVE_FAILED_TIMES); Assertions.assertEquals("maxSingleTestWindow", Configuration.FILTER_MAX_SINGLE_TEST_WINDOW); Assertions.assertNotNull(Configuration.INSTANCE); } @Test public void testFullConfigurationWithArgsString() { Assertions.assertNotNull(Configuration.INSTANCE.getSuccessiveFailedTimes("test")); Assertions.assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds("test")); } @Test public void testGetSuccessiveFailedTimes() { Assertions.assertNotNull(Configuration.INSTANCE.getSuccessiveFailedTimes("test")); } @Test public void testGetSessionTimeoutInSeconds() { Assertions.assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds("test")); } @Test public void testGetMaxSingleTestWindow() { Assertions.assertEquals(60000, Configuration.INSTANCE.getMaxSingleTestWindow()); Mockito.when(environment.getProperty("servicecomb.loadbalance.isolation.maxSingleTestWindow")).thenReturn("5000"); Assertions.assertEquals(5000, Configuration.INSTANCE.getMaxSingleTestWindow()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestExtensionsManager { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { LegacyPropertyFactory.setEnvironment(environment); } @AfterEach public void tearDown() { } @Test public void testRuleName() { List extensionsFactories = new ArrayList<>(); extensionsFactories.add(new RuleNameExtensionsFactory()); ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); Assertions.assertEquals(RoundRobinRuleExt.class.getName(), extensionsManager.createLoadBalancerRule("RoundRobin").getClass().getName()); Assertions.assertEquals(RandomRuleExt.class.getName(), extensionsManager.createLoadBalancerRule("Random").getClass().getName()); Assertions.assertEquals(WeightedResponseTimeRuleExt.class.getName(), extensionsManager.createLoadBalancerRule("WeightedResponse").getClass().getName()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import mockit.Expectations; import mockit.Injectable; public class TestLoadBalanceCreator { @Test public void testLoadBalanceWithRoundRobinRuleAndFilter(@Injectable Invocation invocation, @Injectable Transport transport) { // Robin components implementations require getReachableServers & getServerList have the same size, we add a test case for this. RoundRobinRuleExt rule = new RoundRobinRuleExt(); List servers = new ArrayList<>(); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance1"); Endpoint host1 = new Endpoint(transport, "host1", instance1); ServiceCombServer server = new ServiceCombServer(null, host1); DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("instance2"); Endpoint host2 = new Endpoint(transport, "host2", instance2); ServiceCombServer server2 = new ServiceCombServer(null, host2); servers.add(server); servers.add(server2); LoadBalancer lb = new LoadBalancer(rule, "test"); List filters = new ArrayList<>(); filters.add((serverList, invocation1) -> { List filteredServers = new ArrayList<>(); for (ServiceCombServer server1 : servers) { if (server1.getHost().equals("host1")) { continue; } filteredServers.add(server1); } return filteredServers; }); lb.setFilters(filters); new Expectations() { { invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); result = servers; } }; ServiceCombServer s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); } @Test public void testLoadBalanceWithRandomRuleAndFilter(@Injectable Invocation invocation, @Injectable Transport transport) { // Robin components implementations require getReachableServers & getServerList have the same size, we add a test case for this. RandomRuleExt rule = new RandomRuleExt(); LoadBalancer lb = new LoadBalancer(rule, "service"); List servers = new ArrayList<>(); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance1"); Endpoint host1 = new Endpoint(transport, "host1", instance1); ServiceCombServer server = new ServiceCombServer(null, host1); DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("instance2"); Endpoint host2 = new Endpoint(transport, "host2", instance2); ServiceCombServer server2 = new ServiceCombServer(null, host2); servers.add(server); servers.add(server2); List filters = new ArrayList<>(); filters.add((serverList, invocation1) -> { List filteredServers = new ArrayList<>(); for (ServiceCombServer server1 : servers) { if (server1.getHost().equals("host1")) { continue; } filteredServers.add(server1); } return filteredServers; }); lb.setFilters(filters); new Expectations() { { invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); result = servers; } }; ServiceCombServer s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); } @Test public void testLoadBalanceWithWeightedResponseTimeRuleAndFilter(@Injectable Endpoint endpoint1, @Injectable Endpoint endpoint2, @Injectable Invocation invocation) { // Robin components implementations require getReachableServers & getServerList have the same size, we add a test case for this. WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); LoadBalancer lb = new LoadBalancer(rule, "service"); List servers = new ArrayList<>(); DiscoveryInstance discoveryInstance1 = Mockito.mock(DiscoveryInstance.class); // StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance1); Mockito.when(discoveryInstance1.getInstanceId()).thenReturn("ii01"); DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("ii02"); new Expectations() { { endpoint1.getEndpoint(); result = "host1"; // endpoint1.getMicroserviceInstance(); // result = instance1; endpoint2.getEndpoint(); result = "host2"; endpoint2.getMicroserviceInstance(); result = instance2; } }; ServiceCombServer server = new ServiceCombServer(null, endpoint1); ServiceCombServer server2 = new ServiceCombServer(null, endpoint2); servers.add(server); servers.add(server2); List filters = new ArrayList<>(); filters.add((serverList, invocation1) -> { List filteredServers = new ArrayList<>(); for (ServiceCombServer server1 : servers) { if (server1.getHost().equals("host1")) { continue; } filteredServers.add(server1); } return filteredServers; }); lb.setFilters(filters); new Expectations() { { invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); result = servers; } }; ServiceCombServer s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); s = lb.chooseServer(invocation); Assertions.assertEquals(server2, s); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import mockit.Deencapsulation; import mockit.Injectable; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; /** * * */ public class TestLoadBalanceFilter { static SCBEngine scbEngine; static TransportManager transportManager; String microserviceName = "ms"; LoadBalanceFilter handler; Map loadBalancerMap; @Injectable Invocation invocation; @Mocked Transport restTransport; Response sendResponse; @Before public void setUp() { Environment environment = Mockito.mock(Environment.class); scbEngine = SCBBootstrap.createSCBEngineForTest(environment); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", boolean.class, false)).thenReturn(false); scbEngine.run(); transportManager = scbEngine.getTransportManager(); new MockUp(invocation) { @Mock String getMicroserviceName() { return microserviceName; } @Mock void next(AsyncResponse asyncResp) throws Exception { asyncResp.handle(sendResponse); } @Mock public T getLocalContext(String key) { return (T) null; } }; new MockUp(transportManager) { @Mock Transport findTransport(String transportName) { return restTransport; } }; List extensionsFactories = new ArrayList<>(); extensionsFactories.add(new RuleNameExtensionsFactory()); ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); DiscoveryTree discoveryTree = new DiscoveryTree( new DiscoveryManager(Collections.emptyList(), List.of(new TelnetInstancePing()))); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancerMap = Deencapsulation.getField(handler, "loadBalancerMap"); } @After public void teardown() { scbEngine.destroy(); } @Test public void testIsFailedResponse() { Assertions.assertFalse(handler.isFailedResponse(Response.create(400, "", ""))); Assertions.assertFalse(handler.isFailedResponse(Response.create(500, "", ""))); Assertions.assertTrue(handler.isFailedResponse(Response.create(490, "", ""))); Assertions.assertTrue(handler.isFailedResponse(Response.consumerFailResp(new NullPointerException()))); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.NonSwaggerInvocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.ZoneAwareDiscoveryFilter; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.discovery.DiscoveryTree; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; public class TestLoadBalanceFilter2 { DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { Mockito.when(environment.getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", boolean.class, false)).thenReturn(false); Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.enabled"), Mockito.any(), Mockito.eq(true))) .thenReturn(true); Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), Mockito.any(), Mockito.eq(30))) .thenReturn(0); Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), Mockito.any(), Mockito.eq(100))) .thenReturn(100); LegacyPropertyFactory.setEnvironment(environment); } @Test public void testZoneAwareFilterWorks() { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Operation operation = Mockito.mock(Operation.class); when(operationMeta.getSwaggerOperation()).thenReturn(operation); when(operation.getExtensions()).thenReturn(null); OpenAPI openAPI = Mockito.mock(OpenAPI.class); when(schemaMeta.getSwagger()).thenReturn(openAPI); when(openAPI.getExtensions()).thenReturn(null); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); when(microserviceMeta.getAppId()).thenReturn("testApp"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test-Region"); myself.setAvailableZone("test-zone"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); DataCenterInfo regionMatchInfo = new DataCenterInfo(); regionMatchInfo.setName("test"); regionMatchInfo.setRegion("test-Region"); regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:9091"); Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); DataCenterInfo noneMatchInfo = new DataCenterInfo(); noneMatchInfo.setName("test"); noneMatchInfo.setRegion("test-Region2"); noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:9092"); Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); when(transportManager.findTransport("rest")).thenReturn(transport); LoadBalanceFilter handler; LoadBalancer loadBalancer; ServiceCombServer server; DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, serverDiscoveryFilter)); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); data.add(noneMatchInstance); parent.cacheVersion(1); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9092", server.getEndpoint().getEndpoint()); data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9091", server.getEndpoint().getEndpoint()); data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9090", server.getEndpoint().getEndpoint()); } @Test public void testIsolationEventWithEndpoint() { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Operation operation = Mockito.mock(Operation.class); when(operationMeta.getSwaggerOperation()).thenReturn(operation); when(operation.getExtensions()).thenReturn(null); OpenAPI openAPI = Mockito.mock(OpenAPI.class); when(schemaMeta.getSwagger()).thenReturn(openAPI); when(openAPI.getExtensions()).thenReturn(null); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); when(microserviceMeta.getAppId()).thenReturn("testApp"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test"); myself.setAvailableZone("test"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test"); info.setAvailableZone("test"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance"); List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); when(transportManager.findTransport("rest")).thenReturn(transport); LoadBalanceFilter handler; LoadBalancer loadBalancer; ServiceCombServer server; DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, serverDiscoveryFilter)); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); data.add(instance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9090", server.getEndpoint().getEndpoint()); } @Test public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); Operation operation = Mockito.mock(Operation.class); when(operationMeta.getSwaggerOperation()).thenReturn(operation); when(operation.getExtensions()).thenReturn(null); OpenAPI openAPI = Mockito.mock(OpenAPI.class); when(schemaMeta.getSwagger()).thenReturn(openAPI); when(openAPI.getExtensions()).thenReturn(null); when(microserviceMeta.getAppId()).thenReturn("testApp"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test-Region"); myself.setAvailableZone("test-zone"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); DataCenterInfo regionMatchInfo = new DataCenterInfo(); regionMatchInfo.setName("test"); regionMatchInfo.setRegion("test-Region"); regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:9091"); Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); DataCenterInfo noneMatchInfo = new DataCenterInfo(); noneMatchInfo.setName("test"); noneMatchInfo.setRegion("test-Region2"); noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:9092"); Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); when(transportManager.findTransport("rest")).thenReturn(transport); LoadBalanceFilter handler; LoadBalancer loadBalancer; ServiceCombServer server; DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, serverDiscoveryFilter)); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); data.add(noneMatchInstance); parent.cacheVersion(1); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9092", server.getEndpoint().getEndpoint()); data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9091", server.getEndpoint().getEndpoint()); data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:9090", server.getEndpoint().getEndpoint()); } @Test public void testZoneAwareFilterUsingMockedInvocationWorks() { Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName"); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.operation.enabled", boolean.class, true)).thenReturn(false); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test-Region"); myself.setAvailableZone("test-zone"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:7090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); DataCenterInfo regionMatchInfo = new DataCenterInfo(); regionMatchInfo.setName("test"); regionMatchInfo.setRegion("test-Region"); regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:7091"); Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); DataCenterInfo noneMatchInfo = new DataCenterInfo(); noneMatchInfo.setName("test"); noneMatchInfo.setRegion("test-Region2"); noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:7092"); Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); when(transportManager.findTransport("rest")).thenReturn(transport); LoadBalanceFilter handler; LoadBalancer loadBalancer; ServiceCombServer server; DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, serverDiscoveryFilter)); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); data.add(noneMatchInstance); parent.cacheVersion(1); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7092", server.getEndpoint().getEndpoint()); data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7091", server.getEndpoint().getEndpoint()); data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7090", server.getEndpoint().getEndpoint()); } @Test public void testStatusFilterUsingMockedInvocationWorks() { Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName"); TransportManager transportManager = Mockito.mock(TransportManager.class); Transport transport = Mockito.mock(Transport.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test-Region"); myself.setAvailableZone("test-zone"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:7090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); Mockito.when(discoveryInstance.getStatus()).thenReturn(MicroserviceInstanceStatus.TESTING); DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); DataCenterInfo regionMatchInfo = new DataCenterInfo(); regionMatchInfo.setName("test"); regionMatchInfo.setRegion("test-Region"); regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:7091"); Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); DataCenterInfo noneMatchInfo = new DataCenterInfo(); noneMatchInfo.setName("test"); noneMatchInfo.setRegion("test-Region2"); noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:7092"); Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); List data = new ArrayList<>(); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); when(transportManager.findTransport("rest")).thenReturn(transport); LoadBalanceFilter handler; LoadBalancer loadBalancer; ServiceCombServer server; DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); Mockito.when(discoveryManager.getOrCreateVersionedCache("testApp", "testMicroserviceName")) .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, serverDiscoveryFilter)); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertNull(server); data.add(noneMatchInstance); parent.cacheVersion(1); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), discoveryTree, scbEngine); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7092", server.getEndpoint().getEndpoint()); data.add(regionMatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7091", server.getEndpoint().getEndpoint()); data.add(allmatchInstance); parent.cacheVersion(parent.cacheVersion() + 1); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = loadBalancer.chooseServer(invocation); Assertions.assertEquals("rest://localhost:7090", server.getEndpoint().getEndpoint()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalancer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestLoadBalancer { private final RuleExt rule = Mockito.mock(RuleExt.class); @Test public void testLoadBalancerFullOperationWithoutException() { List newServers = new ArrayList<>(); ServiceCombServer server = Mockito.mock(ServiceCombServer.class); Invocation invocation = Mockito.mock(Invocation.class); StatefulDiscoveryInstance microserviceInstance = Mockito.mock(StatefulDiscoveryInstance.class); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); newServers.add(server); Mockito.when(invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST)).thenReturn(newServers); Mockito.when(server.getInstance()).thenReturn(microserviceInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("123456"); LoadBalancer loadBalancer = new LoadBalancer(rule, "test"); loadBalancer.chooseServer(invocation); Mockito.when(rule.choose(newServers, invocation)).thenReturn(server); Assertions.assertEquals(server, loadBalancer.chooseServer(invocation)); Assertions.assertEquals("test", loadBalancer.getMicroServiceName()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestRoundRobinRuleExt { @Test public void testRoundRobin() { RoundRobinRuleExt rule = new RoundRobinRuleExt(); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 2; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); Mockito.when(server.toString()).thenReturn("server " + i); servers.add(server); } AtomicInteger server1 = new AtomicInteger(0); AtomicInteger server2 = new AtomicInteger(0); for (int i = 0; i < 2000; i++) { if (rule.choose(servers, invocation).toString().equals("server 0")) { server1.incrementAndGet(); } else { server2.incrementAndGet(); } } Assertions.assertEquals(server1.get(), server2.get()); } @Test public void testBenchmarkRobin() { // less than 0.001ms RoundRobinRuleExt rule = new RoundRobinRuleExt(); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 100; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); Mockito.when(server.toString()).thenReturn("server " + i); servers.add(server); } long begin = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { rule.choose(servers, invocation); } long taken = System.currentTimeMillis() - begin; System.out.println("taken " + taken); Assertions.assertTrue(taken < 10 * 5, "actual token " + taken); // 5 * times make slow machine happy } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; /** * * */ public class TestServiceCombServer { private final Transport transport = Mockito.mock(Transport.class); private ServiceCombServer cs; @Before public void setUp() { DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("123456"); cs = new ServiceCombServer(null, new Endpoint(transport, "abcd", instance)); } @Test public void testCseServerObj() { Assertions.assertNotNull(cs); } @Test public void testGetEndpoint() { cs.getEndpoint(); Assertions.assertNotNull(cs.getEndpoint()); } @Test public void testEqualsMethod() { Assertions.assertNotEquals(cs, (Object) "abcd"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("1234"); ServiceCombServer other = new ServiceCombServer(null, new Endpoint(transport, "1234", instance1)); Assertions.assertNotEquals(cs, other); DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("123456"); other = new ServiceCombServer(null, new Endpoint(transport, "abcd", instance2)); Assertions.assertEquals(cs, other); } @Test public void testToStringMethod() { cs.toString(); Assertions.assertNotNull(cs.toString()); } @Test public void testGetHost() { cs.getHost(); Assertions.assertNotNull(cs.getHost()); } @Test public void testHashCodeMethod() { cs.hashCode(); Assertions.assertNotNull(cs.hashCode()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import mockit.Deencapsulation; import mockit.Mock; import mockit.MockUp; public class TestSessionStickinessRule { @Test public void testServerWithoutTimeoutAndWithThreshold() { boolean status = true; SessionStickinessRule ss = new SessionStickinessRule(); Invocation invocation = mock(Invocation.class); ServiceCombServer server = mock(ServiceCombServer.class); List servers = new ArrayList<>(); servers.add(server); Deencapsulation.setField(ss, "lastServer", server); ServerMetrics serverMetrics = new ServerMetrics(); Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); new MockUp() { @Mock private boolean isTimeOut() { return false; } }; new MockUp() { @Mock private boolean isErrorThresholdMet(ServiceCombServer server) { return true; } }; try { ss.choose(servers, invocation); } catch (Exception e) { e.printStackTrace(); status = false; } Assertions.assertTrue(status); } @Test public void testServerWithTimeout() { boolean status = true; SessionStickinessRule ss = new SessionStickinessRule(); Invocation invocation = mock(Invocation.class); ServiceCombServer server = mock(ServiceCombServer.class); List servers = new ArrayList<>(); servers.add(server); Deencapsulation.setField(ss, "lastServer", server); new MockUp() { @Mock private boolean isTimeOut() { return true; } }; try { ss.choose(servers, invocation); } catch (Exception e) { status = false; } Assertions.assertTrue(status); } @Test public void testServerWithoutTimeoutException() { boolean status = true; SessionStickinessRule ss = new SessionStickinessRule(); Invocation invocation = mock(Invocation.class); ServiceCombServer server = mock(ServiceCombServer.class); List servers = new ArrayList<>(); servers.add(server); Deencapsulation.setField(ss, "lastServer", server); new MockUp() { @Mock private boolean isTimeOut() { return false; } }; try { ss.choose(servers, invocation); } catch (Exception e) { status = false; } Assertions.assertFalse(status); } @Test public void testServerWithoutTimeoutAndThreshold() { boolean status = true; SessionStickinessRule ss = new SessionStickinessRule(); Invocation invocation = mock(Invocation.class); ServiceCombServer server = mock(ServiceCombServer.class); List servers = new ArrayList<>(); servers.add(server); Deencapsulation.setField(ss, "lastServer", server); new MockUp() { @Mock private boolean isTimeOut() { return false; } }; new MockUp() { @Mock private boolean isErrorThresholdMet(ServiceCombServer server) { return false; } }; new MockUp() { @Mock private boolean isLastServerExists(ServiceCombServer server) { return true; } }; try { ss.choose(servers, invocation); } catch (Exception e) { status = false; } Assertions.assertTrue(status); } @Test public void testLastServerNotExist() { SessionStickinessRule rule = new SessionStickinessRule(); Transport transport = mock(Transport.class); Invocation invocation = mock(Invocation.class); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance1 = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("1234"); ServiceCombServer mockedServer = new ServiceCombServer(null, new Endpoint(transport, "rest:127.0.0.1:8890", instance1)); List allServers = Arrays.asList(mockedServer); LoadBalancer lb = new LoadBalancer(rule, "mockedServer"); when(invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST)).thenReturn(allServers); rule.setLoadBalancer(lb); ServiceCombServer server = new ServiceCombServer(null, new Endpoint(transport, "rest:127.0.0.1:8890", instance1)); Deencapsulation.setField(rule, "lastServer", server); new MockUp(rule) { @Mock private boolean isTimeOut() { return false; } @Mock private boolean isErrorThresholdMet(ServiceCombServer server) { return false; } }; ServiceCombServer s = rule.choose(allServers, invocation); Assertions.assertEquals(mockedServer, s); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.github.resilience4j.core.metrics.Metrics.Outcome; public class TestWeightedResponseTimeRuleExt { @Test public void testRoundRobin() { WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 2; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); ServerMetrics serverMetrics = new ServerMetrics(); Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); Mockito.when(server.toString()).thenReturn("server " + i); servers.add(server); serverMetrics.record(1, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } AtomicInteger server1 = new AtomicInteger(0); AtomicInteger server2 = new AtomicInteger(0); for (int i = 0; i < 2000; i++) { if (rule.choose(servers, invocation).toString().equals("server 0")) { server1.incrementAndGet(); } else { server2.incrementAndGet(); } } Assertions.assertEquals(server1.get(), server2.get()); } @Test public void testWeighed() { WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); ServiceCombServer server1 = Mockito.mock(ServiceCombServer.class); Mockito.when(server1.toString()).thenReturn("server " + 0); servers.add(server1); ServerMetrics serverMetrics1 = new ServerMetrics(); Mockito.when(server1.getServerMetrics()).thenReturn(serverMetrics1); ServiceCombServer server2 = Mockito.mock(ServiceCombServer.class); Mockito.when(server2.toString()).thenReturn("server " + 1); servers.add(server2); ServerMetrics serverMetrics2 = new ServerMetrics(); Mockito.when(server2.getServerMetrics()).thenReturn(serverMetrics2); AtomicInteger serverCounter1 = new AtomicInteger(0); AtomicInteger serverCounter2 = new AtomicInteger(0); for (int i = 0; i < 100; i++) { serverMetrics1.record(200, TimeUnit.MILLISECONDS, Outcome.SUCCESS); serverMetrics2.record(400, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } for (int i = 0; i < 2000; i++) { if (rule.choose(servers, invocation).toString().equals("server 0")) { serverCounter1.incrementAndGet(); } else { serverCounter2.incrementAndGet(); } } double percent = (double) serverCounter1.get() / (serverCounter2.get() + serverCounter1.get()); System.out.println("percent" + percent); Assertions.assertEquals(0.67d, percent, 0.1); serverCounter1.set(0); serverCounter2.set(0); for (int i = 0; i < 100; i++) { serverMetrics1.record(20, TimeUnit.MILLISECONDS, Outcome.SUCCESS); serverMetrics2.record(20, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } for (int i = 0; i < 2000; i++) { if (rule.choose(servers, invocation).toString().equals("server 0")) { serverCounter1.incrementAndGet(); } else { serverCounter2.incrementAndGet(); } } percent = (double) serverCounter1.get() / (serverCounter2.get() + serverCounter1.get()); System.out.println("percent" + percent); Assertions.assertEquals(0.50d, percent, 0.1); } @Test public void testBenchmark() { // 100 instances will taken less than 0.1ms. Because we use weighed rule when response time more than 10ms, // This only taken 1/1000 time. WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 100; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); ServerMetrics serverMetrics = new ServerMetrics(); Mockito.when(server.toString()).thenReturn("server " + i); Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); servers.add(server); serverMetrics.record(i, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } long begin = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { rule.choose(servers, invocation); } long taken = System.currentTimeMillis() - begin; System.out.println("taken " + taken); Assertions.assertTrue(taken < 1000 * 5, "actually taken: " + taken); // 5 * times make slow machine happy } @Test public void testBenchmarkRobin() { // 100 instances will taken less than 0.02ms. Not as good as RoundRobinRule, which taken less than 0.001ms WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); List servers = new ArrayList<>(); Invocation invocation = Mockito.mock(Invocation.class); for (int i = 0; i < 100; i++) { ServiceCombServer server = Mockito.mock(ServiceCombServer.class); ServerMetrics serverMetrics = new ServerMetrics(); Mockito.when(server.toString()).thenReturn("server " + i); Mockito.when(server.getServerMetrics()).thenReturn(serverMetrics); servers.add(server); serverMetrics.record(2, TimeUnit.MILLISECONDS, Outcome.SUCCESS); } long begin = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { rule.choose(servers, invocation); } long taken = System.currentTimeMillis() - begin; System.out.println("taken " + taken); Assertions.assertTrue(taken < 200 * 5, "actually taken: " + taken); // 5 * times make slow machine happy } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.exception; import org.apache.servicecomb.core.exception.CseException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestLoadbalanceExceptionUtils { @Test public void testLoadbalanceExceptionUtils() { Assertions.assertEquals("servicecomb.handler.lb.wrong.rule", LoadbalanceExceptionUtils.CSE_HANDLER_LB_WRONG_RULE); CseException cseException = LoadbalanceExceptionUtils.createLoadbalanceException("servicecomb.handler.lb.wrong.rule", new Throwable(), "ARGS"); Assertions.assertNotNull(cseException); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; import com.google.common.collect.Sets; /** * Test for PriorityInstancePropertyDiscoveryFilter */ public class PriorityInstancePropertyDiscoveryFilterTest { public static final String PROPERTY_KEY = "environment"; private PriorityInstancePropertyDiscoveryFilter filter; private List instances; StatefulDiscoveryInstance instance1; ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); EnumerablePropertySource propertySource; DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); @Before public void setUp() { propertySource = Mockito.mock(EnumerablePropertySource.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.service.properties." + PROPERTY_KEY }); filter = new PriorityInstancePropertyDiscoveryFilter(); filter.setEnvironment(environment); filter.setDynamicProperties(dynamicProperties); Mockito.when(dynamicProperties.getStringProperty(Mockito.eq("servicecomb.loadbalance.filter.priorityInstanceProperty.key"), Mockito.any(), Mockito.eq("environment"))).thenReturn("environment"); instances = new ArrayList<>(); filter.setEnvironment(environment); DiscoveryInstance discoveryInstance1 = Mockito.mock(DiscoveryInstance.class); instance1 = new StatefulDiscoveryInstance(discoveryInstance1); Mockito.when(discoveryInstance1.getInstanceId()).thenReturn("instance.empty"); DiscoveryInstance discoveryInstance2 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance2 = new StatefulDiscoveryInstance(discoveryInstance2); Mockito.when(discoveryInstance2.getInstanceId()).thenReturn("instance.local"); Map properties = new HashMap<>(); properties.put(PROPERTY_KEY, "local"); Mockito.when(discoveryInstance2.getProperties()).thenReturn(properties); DiscoveryInstance discoveryInstance3 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance3 = new StatefulDiscoveryInstance(discoveryInstance3); Mockito.when(discoveryInstance3.getInstanceId()).thenReturn("instance.local.feature1"); Map properties3 = new HashMap<>(); properties3.put(PROPERTY_KEY, "local.feature1"); Mockito.when(discoveryInstance3.getProperties()).thenReturn(properties3); DiscoveryInstance discoveryInstance4 = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance4 = new StatefulDiscoveryInstance(discoveryInstance4); Mockito.when(discoveryInstance4.getInstanceId()).thenReturn("instance.local.feature1.sprint1"); Map properties4 = new HashMap<>(); properties4.put(PROPERTY_KEY, "local.feature1.sprint1"); Mockito.when(discoveryInstance4.getProperties()).thenReturn(properties4); instances.add(instance1); instances.add(instance2); instances.add(instance3); instances.add(instance4); } @After public void cleanup() { } @Test public void testGetFilteredListOfServers() { //complete match executeTest("", Sets.newHashSet("instance.empty")); executeTest("local", Sets.newHashSet("instance.local")); executeTest("local.feature1", Sets.newHashSet("instance.local.feature1")); executeTest("local.feature1.sprint1", Sets.newHashSet("instance.local.feature1.sprint1")); //priority match executeTest("test", Sets.newHashSet("instance.empty")); executeTest("local.feature2", Sets.newHashSet("instance.local")); executeTest("local.feature1.sprint2", Sets.newHashSet("instance.local.feature1")); executeTest("local.feature2.sprint1", Sets.newHashSet("instance.local")); executeTest("local.feature1.sprint2.temp", Sets.newHashSet("instance.local.feature1")); //none match instances.remove(instance1); executeTest("", Collections.emptySet()); executeTest("foo", Collections.emptySet()); instances.add(instance1); } private void executeTest(String selfProperty, Set expectedMatchedKeys) { Mockito.when(environment.getProperty("servicecomb.service.properties." + PROPERTY_KEY)).thenReturn(selfProperty); Invocation invocation = new Invocation(); DiscoveryContext discoveryContext = new DiscoveryContext(); discoveryContext.setInputParameters(invocation); DiscoveryTreeNode parent = new DiscoveryTreeNode(); parent.name("parent"); parent.data(instances); DiscoveryTreeNode node = filter.discovery(discoveryContext, parent); List filterInstance = node.data(); assertThat(filterInstance.stream().map(instance -> instance.getInstanceId()).collect( Collectors.toList())).containsAnyElementsOf(expectedMatchedKeys); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestInstancePropertyDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; public class TestInstancePropertyDiscoveryFilter { private InstancePropertyDiscoveryFilter filter; StatefulDiscoveryInstance instance; ConfigurableEnvironment environment; EnumerablePropertySource propertySource; @BeforeEach public void setUp() { environment = Mockito.mock(ConfigurableEnvironment.class); LegacyPropertyFactory.setEnvironment(environment); propertySource = Mockito.mock(EnumerablePropertySource.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.loadbalance.test.flowsplitFilter.policy", "servicecomb.loadbalance.test.flowsplitFilter.options.tag0" }); Mockito.when(environment.getProperty("servicecomb.loadbalance.test.flowsplitFilter.policy")) .thenReturn("org.apache.servicecomb.loadbalance.filter.SimpleFlowsplitFilter"); Mockito.when(environment.getProperty("servicecomb.loadbalance.test.flowsplitFilter.options.tag0")) .thenReturn("value0"); filter = new InstancePropertyDiscoveryFilter(); Map properties = new HashMap<>(); properties.put("tag0", "value0"); properties.put("tag1", "value1"); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); instance = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance111"); Mockito.when(discoveryInstance.getProperties()).thenReturn(properties); } @Test public void testAllowVisit() { Map filterOptions = new HashMap<>(); Assertions.assertTrue(filter.allowVisit(instance, filterOptions)); filterOptions.put("tag0", "value0"); Assertions.assertTrue(filter.allowVisit(instance, filterOptions)); filterOptions.put("tag2", "value2"); Assertions.assertFalse(filter.allowVisit(instance, filterOptions)); filterOptions.clear(); filterOptions.put("tag0", "value1"); Assertions.assertFalse(filter.allowVisit(instance, filterOptions)); } @Test @SuppressWarnings("unchecked") public void testGetFilteredListOfServers() { DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode parent = new DiscoveryTreeNode(); Invocation invocation = Mockito.mock(Invocation.class); context.setInputParameters(invocation); List instances = new ArrayList<>(); instances.add(instance); parent.data(instances); parent.name("parent"); DiscoveryTreeNode node = filter.discovery(context, parent); Assertions.assertEquals(1, ((List) node.data()).size()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestServerDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import static org.mockito.ArgumentMatchers.any; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestServerDiscoveryFilter { ServerDiscoveryFilter filter = new ServerDiscoveryFilter(); @Test public void createEndpoint_TransportNotExist() { TransportManager transportManager = Mockito.mock(TransportManager.class); Mockito.when(transportManager.findTransport(any(String.class))).thenReturn(null); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); filter.setScbEngine(scbEngine); ServiceCombServer server = (ServiceCombServer) filter.createEndpoint(null, CoreConst.RESTFUL, null, null); Assertions.assertNull(server); } @Test public void createEndpointNormal() { TransportManager transportManager = Mockito.mock(TransportManager.class); SCBEngine scbEngine = Mockito.mock(SCBEngine.class); Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); Transport transport = Mockito.mock(Transport.class); Invocation invocation = Mockito.mock(Invocation.class); Mockito.when(transportManager.findTransport(any(String.class))).thenReturn(transport); Mockito.when(invocation.getConfigTransportName()).thenReturn(CoreConst.RESTFUL); Mockito.when(invocation.getMicroserviceName()).thenReturn("test"); DiscoveryContext context = new DiscoveryContext(); context.setInputParameters(invocation); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance instance = new StatefulDiscoveryInstance(discoveryInstance); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("0000001"); filter.setScbEngine(scbEngine); ServiceCombServer server = (ServiceCombServer) filter .createEndpoint(context, CoreConst.RESTFUL, "rest://localhost:8080", instance); Assertions.assertSame(instance, server.getInstance()); Assertions.assertSame(transport, server.getEndpoint().getTransport()); Assertions.assertEquals("rest://localhost:8080", server.getEndpoint().getEndpoint()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.loadbalance.filter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestZoneAwareDiscoveryFilter { DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); @BeforeEach public void setUp() { Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.enabled"), Mockito.any(), Mockito.eq(true))) .thenReturn(true); } @Test public void test_not_enough_instance() { Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), Mockito.any(), Mockito.eq(30))) .thenReturn(50); Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), Mockito.any(), Mockito.eq(50))) .thenReturn(70); ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter(); filter.setDynamicProperties(dynamicProperties); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test-Region"); myself.setAvailableZone("test-zone"); filter.setDataCenterProperties(myself); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); DataCenterInfo regionMatchInfo = new DataCenterInfo(); regionMatchInfo.setName("test"); regionMatchInfo.setRegion("test-Region"); regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:9091"); Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); DataCenterInfo noneMatchInfo = new DataCenterInfo(); noneMatchInfo.setName("test"); noneMatchInfo.setRegion("test-Region2"); noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:9092"); Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); // run test List data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode result = filter.discovery(context, parent); // check result Integer level = context.getContextParameter(filter.contextParameter()); Integer groups = parent.attribute(filter.groupsSizeParameter()); List resultData = result.data(); Assertions.assertEquals(1, level); Assertions.assertEquals(2, groups); Assertions.assertEquals(2, resultData.size()); Assertions.assertEquals("regionMatchInstance", resultData.get(0).getInstanceId()); Assertions.assertEquals("allmatchInstance", resultData.get(1).getInstanceId()); } @Test public void test_not_enough_instance_both_ceiling_floor() { Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), Mockito.any(), Mockito.eq(30))) .thenReturn(40); Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), Mockito.any(), Mockito.eq(60))) .thenReturn(60); ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter(); filter.setDynamicProperties(dynamicProperties); // set up data DataCenterProperties myself = new DataCenterProperties(); myself.setName("test"); myself.setRegion("test-Region"); myself.setAvailableZone("test-zone"); filter.setDataCenterProperties(myself); DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); DataCenterInfo info = new DataCenterInfo(); info.setName("test"); info.setRegion("test-Region"); info.setAvailableZone("test-zone"); List allMatchEndpoint = new ArrayList<>(); allMatchEndpoint.add("rest://localhost:9090"); Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); DataCenterInfo regionMatchInfo = new DataCenterInfo(); regionMatchInfo.setName("test"); regionMatchInfo.setRegion("test-Region"); regionMatchInfo.setAvailableZone("test-zone2"); List regionMatchEndpoint = new ArrayList<>(); regionMatchEndpoint.add("rest://localhost:9091"); Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); DataCenterInfo noneMatchInfo = new DataCenterInfo(); noneMatchInfo.setName("test"); noneMatchInfo.setRegion("test-Region2"); noneMatchInfo.setAvailableZone("test-zone2"); List noMatchEndpoint = new ArrayList<>(); noMatchEndpoint.add("rest://localhost:9092"); Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); // run test List data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance); DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); DiscoveryContext context = new DiscoveryContext(); DiscoveryTreeNode result = filter.discovery(context, parent); // check result Integer level = context.getContextParameter(filter.contextParameter()); Integer groups = parent.attribute(filter.groupsSizeParameter()); List resultData = result.data(); Assertions.assertEquals(null, level); Assertions.assertEquals(1, groups); Assertions.assertEquals(3, resultData.size()); Assertions.assertEquals("noneMatchInstance", resultData.get(0).getInstanceId()); Assertions.assertEquals("regionMatchInstance", resultData.get(1).getInstanceId()); Assertions.assertEquals("allmatchInstance", resultData.get(2).getInstanceId()); } } ================================================ FILE: handlers/handler-loadbalance/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: filter-chains: consumer: default: empty producer: default: empty ================================================ FILE: handlers/handler-publickey-auth/pom.xml ================================================ 4.0.0 org.apache.servicecomb handlers 3.4.0-SNAPSHOT handler-publickey-auth Java Chassis::Handlers::PublicKey Auth UTF-8 org.apache.servicecomb java-chassis-core org.apache.servicecomb registry-local test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test org.mockito mockito-inline test ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.foundation.common.utils.KeyPairEntry; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.apache.servicecomb.foundation.token.Keypair4Auth; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.springframework.beans.factory.annotation.Autowired; /** * initialize public and private key pair when system boot before registry instance to service center */ public class AuthenticationBootListener implements BootListener { private RegistrationManager registrationManager; @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; } @Override public void onBootEvent(BootEvent event) { if (!EventType.BEFORE_REGISTRY.equals(event.getEventType())) { return; } KeyPairEntry rsaKeyPairEntry = KeyPairUtils.generateALGKeyPair(); Keypair4Auth.INSTANCE.setPrivateKey(rsaKeyPairEntry.getPrivateKey()); Keypair4Auth.INSTANCE.setPublicKey(rsaKeyPairEntry.getPublicKey()); Keypair4Auth.INSTANCE.setPublicKeyEncoded(rsaKeyPairEntry.getPublicKeyEncoded()); this.registrationManager.addProperty(DefinitionConst.INSTANCE_PUBKEY_PRO, rsaKeyPairEntry.getPublicKeyEncoded()); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication; import org.apache.servicecomb.authentication.consumer.ConsumerAuthFilter; import org.apache.servicecomb.authentication.consumer.ConsumerTokenManager; import org.apache.servicecomb.authentication.provider.AccessController; import org.apache.servicecomb.authentication.provider.ProviderAuthFilter; import org.apache.servicecomb.authentication.provider.ProviderTokenManager; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration @ConditionalOnProperty(value = AuthenticationConfiguration.ACCESS_CONTROL_ENABLED, havingValue = "true") public class AuthenticationConfiguration { public static final String ACCESS_CONTROL_PREFIX = "servicecomb.publicKey.accessControl"; public static final String ACCESS_CONTROL_ENABLED = ACCESS_CONTROL_PREFIX + ".enabled"; @Bean public ConsumerAuthFilter scbConsumerAuthFilter() { return new ConsumerAuthFilter(); } @Bean public ProviderAuthFilter scbProviderAuthFilter() { return new ProviderAuthFilter(); } @Bean public AuthenticationBootListener scbAuthenticationBootListener() { return new AuthenticationBootListener(); } @Bean public ConsumerTokenManager scbConsumerTokenManager() { return new ConsumerTokenManager(); } @Bean public ProviderTokenManager scbProviderTokenManager() { return new ProviderTokenManager(); } @Bean public AccessController scbAccessController(Environment environment) { return new AccessController(environment); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/RSAAuthenticationToken.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication; /** * token 组成部分: * token: instanceId@@generateTime@randomCode@sign(instanceId@@generateTime@randomCode) * */ public class RSAAuthenticationToken { public static final long TOKEN_ACTIVE_TIME = 24 * 60 * 60 * 1000; private final String instanceId; private final String serviceId; private final long generateTime; private final String randomCode; private final String sign; private final String tokenFormat; private final String plainToken; public RSAAuthenticationToken(String instanceId, String serviceId, long generateTime, String randomCode, String sign) { this.instanceId = instanceId; this.generateTime = generateTime; this.randomCode = randomCode; this.serviceId = serviceId; this.sign = sign; this.tokenFormat = String.format("%s@%s@%s@%s@%s", instanceId, serviceId, generateTime, randomCode, sign); this.plainToken = String.format("%s@%s@%s@%s", this.instanceId, this.serviceId, this.generateTime, this.randomCode); } public String plainToken() { return this.plainToken; } public String getInstanceId() { return instanceId; } public long getGenerateTime() { return generateTime; } public String getSign() { return sign; } public String format() { return tokenFormat; } public static RSAAuthenticationToken fromStr(String token) { String[] tokenArr = token.split("@"); if (tokenArr.length != 5) { return null; } return new RSAAuthenticationToken(tokenArr[0], tokenArr[1], Long.parseLong(tokenArr[2]), tokenArr[3], tokenArr[4]); } public String getServiceId() { return serviceId; } @Override public boolean equals(Object obj) { if (!(obj instanceof RSAAuthenticationToken)) { return false; } RSAAuthenticationToken token = (RSAAuthenticationToken) obj; if (!token.plainToken().equals(this.plainToken())) { return false; } return token.getSign().equals(this.sign); } public int hashCode() { return this.plainToken().hashCode() + this.sign.hashCode(); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.consumer; import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import jakarta.ws.rs.core.Response.Status; public class ConsumerAuthFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private ConsumerTokenManager authenticationTokenManager; @Autowired public void setConsumerTokenManager(ConsumerTokenManager consumerTokenManager) { this.authenticationTokenManager = consumerTokenManager; } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1010; } @Override public String getName() { return "consumer-public-key"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Optional token = Optional.ofNullable(authenticationTokenManager.getToken()); if (!token.isPresent()) { return CompletableFuture.failedFuture( new InvocationException(Status.SERVICE_UNAVAILABLE, "auth token is not properly configured yet.")); } invocation.addContext(CoreConst.AUTH_TOKEN, token.get()); return nextNode.onFilter(invocation); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerTokenManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.consumer; import java.security.PrivateKey; import org.apache.servicecomb.authentication.RSAAuthenticationToken; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.apache.servicecomb.foundation.token.Keypair4Auth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class ConsumerTokenManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerTokenManager.class); private final Object lock = new Object(); private RSAAuthenticationToken token; private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } public String getToken() { if (isExpired(token)) { synchronized (lock) { if (isExpired(token)) { return createToken(); } } } return token.format(); } public String createToken() { PrivateKey privateKey = Keypair4Auth.INSTANCE.getPrivateKey(); String instanceId = BootStrapProperties.readServiceName(environment); String serviceId = BootStrapProperties.readApplication(environment); if (instanceId == null || serviceId == null) { LOGGER.error("service not ready when create token."); return null; } @SuppressWarnings("deprecation") String randomCode = org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(128); long generateTime = System.currentTimeMillis(); try { String plain = String.format("%s@%s@%s@%s", instanceId, serviceId, generateTime, randomCode); String sign = KeyPairUtils.sign(plain, privateKey); token = RSAAuthenticationToken.fromStr(String.format("%s@%s", plain, sign)); } catch (Exception e) { LOGGER.error("create token error", e); return null; } return token.format(); } /** * the TTL of Token is 24 hours * client token will expired 15 minutes early */ public boolean isExpired(RSAAuthenticationToken token) { if (null == token) { return true; } long generateTime = token.getGenerateTime(); long expiredDate = generateTime + RSAAuthenticationToken.TOKEN_ACTIVE_TIME - 15 * 60 * 1000; long now = System.currentTimeMillis(); return now > expiredDate; } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/AccessController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.provider; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; /** * Add black / white list control to service access */ public class AccessController { static class ConfigurationItem { static final String CATEGORY_PROPERTY = "property"; String category; String propertyName; String rule; } private static final Logger LOG = LoggerFactory.getLogger(AccessController.class); private static final String KEY_WHITE_LIST_PREFIX = "servicecomb.publicKey.accessControl.white"; private static final String KEY_BLACK_LIST_PREFIX = "servicecomb.publicKey.accessControl.black"; private static final String KEY_PROPERTY_NAME = "%s.%s.propertyName"; private static final String KEY_CATEGORY = "%s.%s.category"; private static final String KEY_RULE_POSTFIX = ".rule"; private final Environment environment; private Map whiteList = new HashMap<>(); private Map blackList = new HashMap<>(); public AccessController(Environment environment) { this.environment = environment; loadConfigurations(KEY_BLACK_LIST_PREFIX); loadConfigurations(KEY_WHITE_LIST_PREFIX); EventManager.register(this); } public boolean isAllowed(DiscoveryInstance microservice) { return whiteAllowed(microservice) && !blackDenied(microservice); } private boolean whiteAllowed(DiscoveryInstance microservice) { if (whiteList.isEmpty()) { return true; } return matchFound(microservice, whiteList); } private boolean blackDenied(DiscoveryInstance microservice) { if (blackList.isEmpty()) { return false; } return matchFound(microservice, blackList); } private boolean matchFound(DiscoveryInstance microservice, Map ruleList) { boolean matched = false; for (ConfigurationItem item : ruleList.values()) { if (ConfigurationItem.CATEGORY_PROPERTY.equals(item.category)) { // we support to configure properties, e.g. serviceName, appId, environment, alias, version and so on, also support key in properties. if (matchMicroserviceField(microservice, item) || matchMicroserviceProperties(microservice, item)) { return true; } } } return matched; } private boolean matchMicroserviceProperties(DiscoveryInstance microservice, ConfigurationItem item) { Map properties = microservice.getProperties(); for (Entry entry : properties.entrySet()) { if (!entry.getKey().equals(item.propertyName)) { continue; } return isPatternMatch(entry.getValue(), item.rule); } return false; } private boolean matchMicroserviceField(DiscoveryInstance microservice, ConfigurationItem item) { String fieldValue; if ("version".equals(item.propertyName)) { fieldValue = microservice.getVersion(); } else if ("serviceName".equals(item.propertyName)) { fieldValue = microservice.getServiceName(); } else { fieldValue = microservice.getProperties().get(item.propertyName); } return isPatternMatch(fieldValue, item.rule); } private boolean isPatternMatch(String value, String pattern) { if (pattern.startsWith("*")) { return value.endsWith(pattern.substring(1)); } if (pattern.endsWith("*")) { return value.startsWith(pattern.substring(0, pattern.length() - 1)); } return value.equals(pattern); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(KEY_WHITE_LIST_PREFIX)) { loadConfigurations(KEY_WHITE_LIST_PREFIX); break; } } for (String changed : event.getChanged()) { if (changed.startsWith(KEY_BLACK_LIST_PREFIX)) { loadConfigurations(KEY_BLACK_LIST_PREFIX); break; } } } private void loadConfigurations(String prefix) { Map configurations = new HashMap<>(); Set configsItems = ConfigUtil.propertiesWithPrefix((ConfigurableEnvironment) environment, prefix); for (String pathKey : configsItems) { if (pathKey.endsWith(KEY_RULE_POSTFIX)) { ConfigurationItem configurationItem = new ConfigurationItem(); String rule = environment.getProperty(pathKey); if (StringUtils.isEmpty(rule)) { continue; } configurationItem.rule = rule; String pathKeyItem = pathKey .substring(prefix.length() + 1, pathKey.length() - KEY_RULE_POSTFIX.length()); configurationItem.propertyName = environment.getProperty(String.format(KEY_PROPERTY_NAME, prefix, pathKeyItem)); if (StringUtils.isEmpty(configurationItem.propertyName)) { continue; } configurationItem.category = environment.getProperty(String.format(KEY_CATEGORY, prefix, pathKeyItem)); if (StringUtils.isEmpty(configurationItem.category)) { continue; } configurations.put(pathKeyItem, configurationItem); } } if (KEY_WHITE_LIST_PREFIX.equals(prefix)) { this.whiteList = configurations; logConfigurations(configurations, true); } else { this.blackList = configurations; logConfigurations(configurations, false); } } private void logConfigurations(Map configurations, boolean isWhite) { configurations.forEach((key, item) -> LOG.info((isWhite ? "White list " : "Black list ") + "config item: key=" + key + ";category=" + item.category + ";propertyName=" + item.propertyName + ";rule=" + item.rule)); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.provider; import org.apache.commons.lang3.StringUtils; import org.springframework.core.env.Environment; public class PathCheckUtils { private static final String KEY_INCLUDE_PATH = "servicecomb.publicKey.accessControl.includePathPatterns"; private static final String KEY_EXCLUDE_PATH = "servicecomb.publicKey.accessControl.excludePathPatterns"; /** * first determine configured non-authentication path is matched requestPath, if match not needed auth. * second determine whether of configured authentication path, if not configured, default all path need auth; * if configured, then check whether of matched requestPath, if match needed auth, otherwise not needed auth. * * @param requestPath path * @param env environment * @return notRequiredAuth */ public static boolean isNotRequiredAuth(String requestPath, Environment env) { if (excludePathMatchPath(requestPath, env)) { return true; } return includePathMatchPath(requestPath, env); } private static boolean excludePathMatchPath(String requestPath, Environment env) { String excludePathPattern = env.getProperty(KEY_EXCLUDE_PATH, ""); if (StringUtils.isEmpty(excludePathPattern)) { return false; } return isPathMather(requestPath, excludePathPattern); } private static boolean includePathMatchPath(String requestPath, Environment env) { String includePathPattern = env.getProperty(KEY_INCLUDE_PATH, ""); if (StringUtils.isEmpty(includePathPattern)) { return false; } return !isPathMather(requestPath, includePathPattern); } private static boolean isPathMather(String requestPath, String pathPattern) { for (String pattern : pathPattern.split(",")) { if (!pattern.isEmpty() && isPatternMatch(requestPath, pattern)) { return true; } } return false; } public static boolean isPatternMatch(String value, String pattern) { if (pattern.startsWith("*") || pattern.startsWith("/*")) { int index = 0; for (int i = 0; i < pattern.length(); i++) { if (pattern.charAt(i) != '*' && pattern.charAt(i) != '/') { break; } index++; } return value.endsWith(pattern.substring(index)); } if (pattern.endsWith("*")) { int index = pattern.length() - 1; for (int i = pattern.length() - 1; i >= 0; i--) { if (pattern.charAt(i) != '*' && pattern.charAt(i) != '/') { break; } index--; } return value.startsWith(pattern.substring(0, index + 1)); } return value.equals(pattern); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.provider; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import jakarta.ws.rs.core.Response.Status; public class ProviderAuthFilter extends AbstractFilter implements ProviderFilter { private ProviderTokenManager authenticationTokenManager; private Environment env; @Autowired public void setProviderTokenManager(ProviderTokenManager providerTokenManager, Environment env) { this.authenticationTokenManager = providerTokenManager; this.env = env; } @Override public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1010; } @Override public String getName() { return "provider-public-key"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { String requestFullPath = SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), invocation.getOperationMeta().getOperationPath()); if (PathCheckUtils.isNotRequiredAuth(requestFullPath, env)) { return nextNode.onFilter(invocation); } String token = invocation.getContext(CoreConst.AUTH_TOKEN); if (null != token && authenticationTokenManager.valid(token)) { return nextNode.onFilter(invocation); } return CompletableFuture.failedFuture( new InvocationException(Status.UNAUTHORIZED, "public key authorization failed.")); } } ================================================ FILE: handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderTokenManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.provider; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.authentication.RSAAuthenticationToken; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.registry.discovery.MicroserviceInstanceCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; public class ProviderTokenManager { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderTokenManager.class); private final Cache validatedToken = CacheBuilder.newBuilder() .expireAfterAccess(getExpiredTime(), TimeUnit.MILLISECONDS) .build(); private AccessController accessController; private MicroserviceInstanceCache microserviceInstanceCache; @Autowired public void setMicroserviceInstanceCache(MicroserviceInstanceCache microserviceInstanceCache) { this.microserviceInstanceCache = microserviceInstanceCache; } @Autowired public void setAccessController(AccessController accessController) { this.accessController = accessController; } public boolean valid(String token) { try { RSAAuthenticationToken rsaToken = RSAAuthenticationToken.fromStr(token); if (null == rsaToken) { LOGGER.error("token format is error, perhaps you need to set auth handler at consumer"); return false; } if (tokenExpired(rsaToken)) { LOGGER.error("token is expired"); return false; } if (validatedToken.asMap().containsKey(rsaToken)) { return accessController.isAllowed(microserviceInstanceCache.getOrCreate( rsaToken.getServiceId(), rsaToken.getInstanceId())); } if (isValidToken(rsaToken) && !tokenExpired(rsaToken)) { validatedToken.put(rsaToken, true); return accessController.isAllowed(microserviceInstanceCache.getOrCreate( rsaToken.getServiceId(), rsaToken.getInstanceId())); } return false; } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | SignatureException e) { LOGGER.error("verify error", e); return false; } } public boolean isValidToken(RSAAuthenticationToken rsaToken) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { String sign = rsaToken.getSign(); String content = rsaToken.plainToken(); String publicKey = getPublicKeyFromInstance(rsaToken.getInstanceId(), rsaToken.getServiceId()); return KeyPairUtils.verify(publicKey, sign, content); } protected int getExpiredTime() { return 60 * 60 * 1000; } private boolean tokenExpired(RSAAuthenticationToken rsaToken) { long generateTime = rsaToken.getGenerateTime(); long expired = generateTime + RSAAuthenticationToken.TOKEN_ACTIVE_TIME + 15 * 60 * 1000; long now = System.currentTimeMillis(); return now > expired; } private String getPublicKeyFromInstance(String instanceId, String serviceId) { DiscoveryInstance instances = microserviceInstanceCache.getOrCreate(serviceId, instanceId); if (instances != null) { return instances.getProperties().get(DefinitionConst.INSTANCE_PUBKEY_PRO); } else { LOGGER.error("not instance found {}-{}, maybe attack", instanceId, serviceId); return ""; } } @VisibleForTesting Cache getValidatedToken() { return validatedToken; } } ================================================ FILE: handlers/handler-publickey-auth/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.authentication.AuthenticationConfiguration ================================================ FILE: handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAccessController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.authentication.provider.AccessController; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; public class TestAccessController { ConfigurableEnvironment environment; EnumerablePropertySource propertySource; @BeforeEach public void tearDown() { environment = Mockito.mock(ConfigurableEnvironment.class); propertySource = Mockito.mock(EnumerablePropertySource.class); } @Test public void testIsValidOfWhiteByServiceName() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.publicKey.accessControl.white.list1.propertyName", "servicecomb.publicKey.accessControl.white.list1.category", "servicecomb.publicKey.accessControl.white.list1.rule" }); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) .thenReturn("serviceName"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) .thenReturn("trust*"); AccessController controller = new AccessController(environment); DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Assertions.assertTrue(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("nottrustCustomer"); Assertions.assertFalse(controller.isAllowed(service)); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) .thenReturn("*trust"); Map latest = new HashMap<>(); latest.put("servicecomb.publicKey.accessControl.white.list1.rule", "*trust"); controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertTrue(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("Customer_trust_not"); Assertions.assertFalse(controller.isAllowed(service)); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) .thenReturn("trust"); latest.put("servicecomb.publicKey.accessControl.white.list1.rule", "trust"); controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trust"); Assertions.assertTrue(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertFalse(controller.isAllowed(service)); } @Test public void testIsValidOfBlackByServiceName() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.publicKey.accessControl.black.list1.propertyName", "servicecomb.publicKey.accessControl.black.list1.category", "servicecomb.publicKey.accessControl.black.list1.rule" }); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) .thenReturn("serviceName"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) .thenReturn("trust*"); AccessController controller = new AccessController(environment); DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Assertions.assertFalse(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("nottrustCustomer"); Assertions.assertTrue(controller.isAllowed(service)); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) .thenReturn("*trust"); Map latest = new HashMap<>(); latest.put("servicecomb.publicKey.accessControl.black.list1.rule", "*trust"); controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertFalse(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("Customer_trust_not"); Assertions.assertTrue(controller.isAllowed(service)); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) .thenReturn("trust"); latest = new HashMap<>(); latest.put("servicecomb.publicKey.accessControl.black.list1.rule", "trust"); controller.onConfigurationChangedEvent(ConfigurationChangedEvent.createIncremental(latest, new HashMap<>())); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trust"); Assertions.assertFalse(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); Assertions.assertTrue(controller.isAllowed(service)); } @Test public void testIsValidOfBlackAndWhiteByServiceName() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.publicKey.accessControl.black.list1.propertyName", "servicecomb.publicKey.accessControl.black.list1.category", "servicecomb.publicKey.accessControl.black.list1.rule", "servicecomb.publicKey.accessControl.white.list1.propertyName", "servicecomb.publicKey.accessControl.white.list1.category", "servicecomb.publicKey.accessControl.white.list1.rule" }); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) .thenReturn("serviceName"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) .thenReturn("trust*"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) .thenReturn("serviceName"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) .thenReturn("*hacker"); AccessController controller = new AccessController(environment); DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Assertions.assertTrue(controller.isAllowed(service)); service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trustCustomerhacker"); Assertions.assertFalse(controller.isAllowed(service)); } @Test public void testIsValidOfBlackByProperties() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.publicKey.accessControl.black.list1.propertyName", "servicecomb.publicKey.accessControl.black.list1.category", "servicecomb.publicKey.accessControl.black.list1.rule", }); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) .thenReturn("tag"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) .thenReturn("test"); AccessController controller = new AccessController(environment); DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Map map = new HashMap<>(); map.put("tag", "test"); Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertFalse(controller.isAllowed(service)); map.put("tag", "testa"); Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertTrue(controller.isAllowed(service)); } @Test public void testIsValidOfWhiteByProperties() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.publicKey.accessControl.white.list1.propertyName", "servicecomb.publicKey.accessControl.white.list1.category", "servicecomb.publicKey.accessControl.white.list1.rule", }); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) .thenReturn("tag"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) .thenReturn("test"); AccessController controller = new AccessController(environment); DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Map map = new HashMap<>(); map.put("tag", "test"); Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertTrue(controller.isAllowed(service)); map.put("tag", "testa"); Mockito.when(service.getProperties()).thenReturn(map); Assertions.assertFalse(controller.isAllowed(service)); } @Test public void testIsValidOfBlackAndWhiteByServiceNameAndVersion() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] { "servicecomb.publicKey.accessControl.black.list1.propertyName", "servicecomb.publicKey.accessControl.black.list1.category", "servicecomb.publicKey.accessControl.black.list1.rule", "servicecomb.publicKey.accessControl.white.list1.propertyName", "servicecomb.publicKey.accessControl.white.list1.category", "servicecomb.publicKey.accessControl.white.list1.rule" }); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.propertyName")) .thenReturn("serviceName"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.white.list1.rule")) .thenReturn("trust*"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.propertyName")) .thenReturn("version"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.category")) .thenReturn("property"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.black.list1.rule")) .thenReturn("0.0.1"); AccessController controller = new AccessController(environment); DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); Mockito.when(service.getVersion()).thenReturn("0.0.1"); Assertions.assertFalse(controller.isAllowed(service)); } } ================================================ FILE: handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAuthenticationBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.BootListener.BootEvent; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.token.Keypair4Auth; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestAuthenticationBootListener { private SCBEngine engine; private Environment environment; @BeforeEach public void setUp() { environment = Mockito.mock(Environment.class); LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) .thenReturn("RSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) .thenReturn("SHA256withRSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) .thenReturn(2048); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)) .thenReturn(BootStrapProperties.DEFAULT_APPLICATION); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_NAME); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_ENVIRONMENT)) .thenReturn(BootStrapProperties.DEFAULT_MICROSERVICE_ENVIRONMENT); engine = SCBBootstrap.createSCBEngineForTest(environment); engine.run(); } @AfterEach public void teardown() { engine.destroy(); } @Test public void testGenerateRSAKey() { RegistrationManager registrationManager = Mockito.mock(RegistrationManager.class); AuthenticationBootListener authenticationBootListener = new AuthenticationBootListener(); authenticationBootListener.setRegistrationManager(registrationManager); BootEvent bootEvent = new BootEvent(); bootEvent.setEventType(BootListener.EventType.BEFORE_REGISTRY); authenticationBootListener.onBootEvent(bootEvent); Assertions.assertNotNull(Keypair4Auth.INSTANCE.getPrivateKey()); Assertions.assertNotNull(Keypair4Auth.INSTANCE.getPublicKey()); } @Test public void testMicroserviceInstancePublicKey() { RegistrationManager registrationManager = Mockito.mock(RegistrationManager.class); AuthenticationBootListener authenticationBootListener = new AuthenticationBootListener(); authenticationBootListener.setRegistrationManager(registrationManager); BootEvent bootEvent = new BootEvent(); bootEvent.setEventType(BootListener.EventType.BEFORE_REGISTRY); authenticationBootListener.onBootEvent(bootEvent); Mockito.verify(registrationManager, times(1)) .addProperty(eq(DefinitionConst.INSTANCE_PUBKEY_PRO), any(String.class)); } } ================================================ FILE: handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestRSAAuthenticationToken.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestRSAAuthenticationToken { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void setUpClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) .thenReturn("RSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) .thenReturn("SHA256withRSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) .thenReturn(2048); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); } @Test public void testRSAAuthenticationToken() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { String tokenstr = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); String contents = token.plainToken(); Assertions.assertEquals( "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ", contents); String sign = token.getSign(); Assertions.assertEquals( "WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk=", sign); String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"; Assertions.assertTrue(KeyPairUtils.verify(pubKey, sign, contents)); } @Test public void testRSAAuthenticationTokenError() { String tokenstr = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); Assertions.assertNull(token); } @Test public void testEqual() { String tokenstr = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); Assertions.assertNotEquals(token, null); RSAAuthenticationToken token2 = RSAAuthenticationToken.fromStr( "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk"); Assertions.assertNotEquals(token2, token); RSAAuthenticationToken token3 = RSAAuthenticationToken.fromStr( "e8a0a4b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk"); Assertions.assertNotEquals(token3, token); RSAAuthenticationToken token4 = RSAAuthenticationToken.fromStr( "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="); Assertions.assertEquals(token4, token); } } ================================================ FILE: handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.provider; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.when; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.servers.Server; import org.apache.servicecomb.swagger.SwaggerUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class TestPathCheckUtils { private static final String KEY_INCLUDE_PATH = "servicecomb.publicKey.accessControl.includePathPatterns"; private static final String KEY_EXCLUDE_PATH = "servicecomb.publicKey.accessControl.excludePathPatterns"; private static final String BASE_PATH = "/api/v1"; private Environment environment; private OpenAPI swagger; @BeforeEach public void setUp() { environment = Mockito.mock(Environment.class); swagger = new OpenAPI(); swagger.setServers(new ArrayList<>()); swagger.getServers().add(new Server().url(BASE_PATH)); } @Test public void testExcludePathWithBasePathAndExactMatch() { String operationPath = "/public"; when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + operationPath); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for excluded path with exact match"); } @Test public void testExcludePathWithBasePathAndWildcard() { when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + "/public/*"); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/test"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for excluded path with wildcard"); fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/nested/path"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for excluded nested path with wildcard"); } @Test public void testIncludePathWithBasePath() { when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(""); when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + "/private/*"); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/private/resource"); assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should require auth for included path"); fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/resource"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for non-included path"); } @Test public void testExcludeOverrideIncludePath() { when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + "/resource"); when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + "/*"); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/resource"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Exclude patterns should override include patterns"); } @Test public void testMultipleExcludePaths() { List operationPaths = List.of("/public", "/health", "/metrics/*"); when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(concatPath(BASE_PATH, operationPaths)); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for first exclude path"); fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/health"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for second exclude path"); fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/metrics/jvm"); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth for wildcard exclude path"); } @Test public void testDifferentBasePath() { String basePath = "/different/base"; String publicOperationPath = "/public"; String privateOperationPath = "/private"; swagger.getServers().clear(); swagger.getServers().add(new Server().url(basePath)); when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(basePath + publicOperationPath); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, publicOperationPath); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth with different base path"); fullPath = SwaggerUtils.concatAbsolutePath(swagger, privateOperationPath); assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should require auth for non-excluded path with different base path"); } @Test public void testNoBasePath() { String operationPath = "/public"; swagger.setServers(null); when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(operationPath); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath); assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should not require auth when no base path is set"); } @Test public void testEmptyConfiguration() { when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(""); when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(""); String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/any/path"); assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), "Should require auth by default when no patterns are configured"); } private String concatPath(String basePath, List paths) { return paths.stream().map(path -> basePath + path).collect(Collectors.joining(",")); } } ================================================ FILE: handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestProviderTokenManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.authentication.provider; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.mockito.ArgumentMatchers.any; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.authentication.RSAAuthenticationToken; import org.apache.servicecomb.authentication.consumer.ConsumerTokenManager; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.KeyPairEntry; import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; import org.apache.servicecomb.foundation.token.Keypair4Auth; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.registry.discovery.MicroserviceInstanceCache; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import com.google.common.cache.Cache; public class TestProviderTokenManager { static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void setUpClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA")) .thenReturn("RSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA")) .thenReturn("SHA256withRSA"); Mockito.when(environment.getProperty("servicecomb.publicKey.accessControl.keySize", int.class, 2048)) .thenReturn(2048); Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); } @BeforeEach public void setUp() { } @AfterEach public void teardown() { } @Test public void testTokenExpired() { String tokenStr = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; ProviderTokenManager tokenManager = new ProviderTokenManager(); DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); Map properties = new HashMap<>(); Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); properties.put(DefinitionConst.INSTANCE_PUBKEY_PRO, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"); Assertions.assertFalse(tokenManager.valid(tokenStr)); } @Test @SuppressWarnings("unchecked") public void testTokenExpiredRemoveInstance() throws Exception { String tokenStr = "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; RSAAuthenticationToken token = Mockito.spy(RSAAuthenticationToken.fromStr(tokenStr)); ProviderTokenManager tokenManager = Mockito.spy(new ProviderTokenManager() { @Override protected int getExpiredTime() { return 500; } }); ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); tokenManager.setAccessController(new AccessController(environment)); MicroserviceInstanceCache microserviceInstanceCache = Mockito.mock(MicroserviceInstanceCache.class); DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); Mockito.when(microserviceInstance.getInstanceId()).thenReturn(""); Map properties = new HashMap<>(); Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); Mockito.when(microserviceInstanceCache.getOrCreate(any(String.class), any(String.class))) .thenReturn(microserviceInstance); tokenManager.setMicroserviceInstanceCache(microserviceInstanceCache); try (MockedStatic rsaAuthenticationTokenMockedStatic = Mockito.mockStatic( RSAAuthenticationToken.class)) { rsaAuthenticationTokenMockedStatic.when(() -> RSAAuthenticationToken.fromStr(tokenStr)).thenReturn(token); Mockito.when(token.getGenerateTime()).thenReturn(System.currentTimeMillis()); Mockito.doReturn(true).when(tokenManager).isValidToken(token); Assertions.assertTrue(tokenManager.valid(tokenStr)); Cache cache = tokenManager .getValidatedToken(); Assertions.assertTrue(cache.asMap().containsKey(token)); Thread.sleep(1000); Assertions.assertFalse(cache.asMap().containsKey(token)); } } @Test public void testTokenFromValidatePool() { KeyPairEntry keyPairEntry = KeyPairUtils.generateALGKeyPair(); Keypair4Auth.INSTANCE.setPrivateKey(keyPairEntry.getPrivateKey()); Keypair4Auth.INSTANCE.setPublicKey(keyPairEntry.getPublicKey()); Keypair4Auth.INSTANCE.setPublicKeyEncoded(keyPairEntry.getPublicKeyEncoded()); String serviceId = "test"; String instanceId = "test"; ConsumerTokenManager consumerTokenManager = new ConsumerTokenManager(); ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_NAME)).thenReturn("test"); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)).thenReturn("test"); consumerTokenManager.setEnvironment(environment); DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); Mockito.when(microserviceInstance.getInstanceId()).thenReturn(instanceId); Map properties = new HashMap<>(); Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); properties.put(DefinitionConst.INSTANCE_PUBKEY_PRO, keyPairEntry.getPublicKeyEncoded()); MicroserviceInstanceCache microserviceInstanceCache = Mockito.mock(MicroserviceInstanceCache.class); Mockito.when(microserviceInstanceCache.getOrCreate(serviceId, instanceId)).thenReturn(microserviceInstance); //Test Consumer first create token String token = consumerTokenManager.getToken(); Assertions.assertNotNull(token); // use cache token Assertions.assertEquals(token, consumerTokenManager.getToken()); ProviderTokenManager rsaProviderTokenManager = new ProviderTokenManager(); MutablePropertySources mutablePropertySources = new MutablePropertySources(); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); rsaProviderTokenManager.setAccessController(new AccessController(environment)); rsaProviderTokenManager.setMicroserviceInstanceCache(microserviceInstanceCache); //first validate need to verify use RSA Assertions.assertTrue(rsaProviderTokenManager.valid(token)); // second validate use validated pool Assertions.assertTrue(rsaProviderTokenManager.valid(token)); } } ================================================ FILE: handlers/handler-publickey-auth/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: filter-chains: consumer: default: empty producer: default: empty ================================================ FILE: handlers/handler-router/pom.xml ================================================ handlers org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 handler-router Java Chassis::Handlers::Router org.springframework spring-core org.springframework spring-beans com.google.guava guava org.yaml snakeyaml org.apache.servicecomb common-rest org.apache.servicecomb servicecomb-governance org.apache.servicecomb handler-loadbalance ================================================ FILE: handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.custom; import java.util.List; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.loadbalance.ServerListFilterExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.router.RouterFilter; import org.apache.servicecomb.router.distribute.RouterDistributor; public class RouterServerListFilter implements ServerListFilterExt { private static final String ENABLE = "servicecomb.router.type"; private static final String TYPE_ROUTER = "router"; @SuppressWarnings("unchecked") private final RouterDistributor routerDistributor = BeanUtils .getBean(RouterDistributor.class); private final RouterFilter routerFilter = BeanUtils.getBean(RouterFilter.class); @Override public boolean enabled() { return LegacyPropertyFactory.getStringProperty(ENABLE, "") .equals(TYPE_ROUTER); } @Override public List getFilteredListOfServers(List list, Invocation invocation) { String targetServiceName = invocation.getMicroserviceName(); return routerFilter.getFilteredListOfServers(list, targetServiceName, MatchType.createGovHttpRequest(invocation), routerDistributor); } } ================================================ FILE: handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.custom; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnProperty(value = ServiceCombRouterConfiguration.ROUTER_ENABLED, havingValue = "true", matchIfMissing = true) public class ServiceCombRouterConfiguration { public static final String ROUTER_PREFIX = "servicecomb.router"; public static final String ROUTER_ENABLED = ROUTER_PREFIX + ".enabled"; @Bean public ServiceCombRouterDistributor scbServiceCombRouterDistributor() { return new ServiceCombRouterDistributor(); } } ================================================ FILE: handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterDistributor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.router.custom; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.router.distribute.AbstractRouterDistributor; public class ServiceCombRouterDistributor extends AbstractRouterDistributor { public ServiceCombRouterDistributor() { init( instance -> instance.getInstance().getVersion(), instance -> instance.getInstance().getServiceName(), instance -> { Map properties = new HashMap<>(); properties.putAll(instance.getInstance().getProperties()); return properties; }); } } ================================================ FILE: handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.router.custom.RouterServerListFilter ================================================ FILE: handlers/handler-router/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.router.custom.ServiceCombRouterConfiguration ================================================ FILE: handlers/handler-tracing-zipkin/pom.xml ================================================ handlers org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 handler-tracing-zipkin Java Chassis::Handlers::Tracing Zipkin org.apache.servicecomb foundation-common org.apache.servicecomb java-chassis-core org.apache.servicecomb common-rest io.zipkin.brave brave-spring-beans io.zipkin.brave brave io.zipkin.brave brave-context-slf4j io.zipkin.zipkin2 zipkin io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.reporter2 zipkin-reporter-brave ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; import brave.SpanCustomizer; import brave.http.HttpRequest; import brave.http.HttpRequestParser; import brave.http.HttpTags; import brave.propagation.TraceContext; public record CustomHttpRequestParser() implements HttpRequestParser { @Override public void parse(HttpRequest req, TraceContext context, SpanCustomizer span) { Invocation invocation = ((InvocationAware) req).getInvocation(); span.name(invocation.getInvocationQualifiedName()); HttpTags.METHOD.tag(req, context, span); HttpTags.PATH.tag(req, context, span); if (!invocation.isConsumer()) { // server or edge HttpTags.ROUTE.tag(req, context, span); } } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; import brave.SpanCustomizer; import brave.Tags; import brave.http.HttpResponse; import brave.http.HttpResponseParser; import brave.http.HttpTags; import brave.propagation.TraceContext; public record CustomHttpResponseParser() implements HttpResponseParser { @Override public void parse(HttpResponse response, TraceContext context, SpanCustomizer span) { Invocation invocation = ((InvocationAware) response).getInvocation(); HttpTags.STATUS_CODE.tag(response, context, span); if (response.error() != null) { span.tag(Tags.ERROR.key(), response.error().getMessage()); } if (!invocation.isProducer()) { // client or edge span.tag(HttpTags.ROUTE.key(), response.route()); } } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; import brave.http.HttpClientRequest; class HttpClientRequestWrapper extends HttpClientRequest implements InvocationAware { private final Invocation invocation; HttpClientRequestWrapper(Invocation invocation) { this.invocation = invocation; } @Override public void header(String name, String value) { invocation.addContext(name, value); } @Override public String method() { return invocation.getOperationMeta().getHttpMethod(); } @Override public String route() { return invocation.getEndpoint().getEndpoint(); } @Override public String path() { return TracingConfiguration.createRequestPath(invocation); } @Override public String url() { return invocation.getInvocationQualifiedName(); } @Override public String header(String name) { return invocation.getContext(name); } @Override public Object unwrap() { return invocation; } @Override public Invocation getInvocation() { return invocation; } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import brave.http.HttpClientRequest; import brave.http.HttpClientResponse; class HttpClientResponseWrapper extends HttpClientResponse implements InvocationAware { final HttpClientRequest request; final Response response; final Invocation invocation; HttpClientResponseWrapper(Invocation invocation, Response response, HttpClientRequestWrapper request) { this.response = response; this.request = request; this.invocation = invocation; } @Override public int statusCode() { return response.getStatusCode(); } @Override public Object unwrap() { return response; } @Override public HttpClientRequest request() { return request; } @Override public Throwable error() { return response.isFailed() ? response.getResult() : null; } @Override public Invocation getInvocation() { return invocation; } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.Invocation; import brave.http.HttpServerRequest; class HttpServeRequestWrapper extends HttpServerRequest implements InvocationAware { private final Invocation invocation; HttpServeRequestWrapper(Invocation invocation) { this.invocation = invocation; } @Override public String method() { return invocation.getOperationMeta().getHttpMethod(); } @Override public String path() { return TracingConfiguration.createRequestPath(invocation); } @Override public String url() { return invocation.getInvocationQualifiedName(); } @Override public String route() { return invocation.getRequestEx() != null ? invocation.getRequestEx().getRemoteAddr() : null; } @Override public String header(String name) { String result = invocation.getContext(name); if (StringUtils.isEmpty(result) && invocation.getRequestEx() != null) { result = invocation.getRequestEx().getHeader(name); } return result; } @Override public Object unwrap() { return invocation; } @Override public Invocation getInvocation() { return invocation; } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; import brave.http.HttpServerRequest; import brave.http.HttpServerResponse; class HttpServerResponseWrapper extends HttpServerResponse implements InvocationAware { private final Response response; private final HttpServerRequest request; private final Invocation invocation; HttpServerResponseWrapper(Invocation invocation, Response response, HttpServeRequestWrapper request) { this.response = response; this.request = request; this.invocation = invocation; } @Override public int statusCode() { return response.getStatusCode(); } @Override public Object unwrap() { return response; } @Override public HttpServerRequest request() { return request; } @Override public Throwable error() { return response.isFailed() ? response.getResult() : null; } @Override public Invocation getInvocation() { return invocation; } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; public interface InvocationAware { Invocation getInvocation(); } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brave.handler.MutableSpan; import brave.handler.SpanHandler; import brave.propagation.TraceContext; public class LogSpanHandler extends SpanHandler { private static final Logger LOGGER = LoggerFactory.getLogger("scb-trace"); public boolean end(TraceContext context, MutableSpan span, Cause cause) { if (!LOGGER.isInfoEnabled()) { return false; } else { LOGGER.info(span.toString()); return true; } } public String toString() { return "LogSpanHandler{name=scb-trace}"; } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_TRACING_COLLECTOR_ADDRESS; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_TRACING_COLLECTOR_API_V1; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_TRACING_COLLECTOR_API_V2; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_TRACING_COLLECTOR_API_VERSION; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_TRACING_COLLECTOR_PATH; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.DEFAULT_TRACING_COLLECTOR_ADDRESS; import java.text.MessageFormat; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import brave.Tracing; import brave.context.slf4j.MDCScopeDecorator; import brave.http.HttpTracing; import brave.propagation.CurrentTraceContext; import brave.propagation.ThreadLocalCurrentTraceContext; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Reporter; import zipkin2.reporter.Sender; import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.brave.AsyncZipkinSpanHandler; import zipkin2.reporter.okhttp3.OkHttpSender; @Configuration @ConditionalOnProperty(value = TracingConfiguration.TRACING_ENABLED, havingValue = "true", matchIfMissing = true) public class TracingConfiguration { public static final String TRACING_PREFIX = "servicecomb.tracing"; public static final String TRACING_ENABLED = TRACING_PREFIX + ".enabled"; public static final String TRACING_REPORTER_LOG_ENABLED = TRACING_PREFIX + ".reporter.log.enabled"; public static final String TRACING_REPORTER_ZIPKIN_ENABLED = TRACING_PREFIX + ".reporter.zipkin.enabled"; @Configuration @ConditionalOnProperty(value = TracingConfiguration.TRACING_REPORTER_ZIPKIN_ENABLED, havingValue = "true") static class ZipkinReporterConfiguration { @Bean BytesMessageSender scbOkHttpSender(DynamicProperties dynamicProperties) { String apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); // use default value if the user set value is invalid if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) != 0) { apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; } String path = MessageFormat.format(CONFIG_TRACING_COLLECTOR_PATH, apiVersion); return OkHttpSender.create( dynamicProperties.getStringProperty( CONFIG_TRACING_COLLECTOR_ADDRESS, DEFAULT_TRACING_COLLECTOR_ADDRESS) .trim() .replaceAll("/+$", "") .concat(path)); } @Bean Reporter scbZipkinReporter(DynamicProperties dynamicProperties, BytesMessageSender sender) { String apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) == 0) { return AsyncReporter.builder(sender).build(SpanBytesEncoder.JSON_V1); } return AsyncReporter.builder(sender).build(); } } @Bean CurrentTraceContext scbCurrentTraceContext() { return ThreadLocalCurrentTraceContext.newBuilder() .addScopeDecorator(MDCScopeDecorator.newBuilder().build()) .build(); } @Bean Tracing scbTracing(@Autowired(required = false) Sender sender, CurrentTraceContext currentTraceContext, Environment environment, DynamicProperties dynamicProperties) { Tracing.Builder builder = Tracing.newBuilder() .localServiceName(BootStrapProperties.readServiceName(environment)) .currentTraceContext(currentTraceContext); // puts trace IDs into logs if (dynamicProperties.getBooleanProperty(TRACING_REPORTER_LOG_ENABLED, true)) { builder.addSpanHandler(new LogSpanHandler()); } if (dynamicProperties.getBooleanProperty(TRACING_REPORTER_ZIPKIN_ENABLED, false)) { builder.addSpanHandler(AsyncZipkinSpanHandler.create(sender)); } return builder.build(); } @Bean HttpTracing scbHttpTracing(Tracing tracing) { return HttpTracing.newBuilder(tracing) .clientRequestParser(new CustomHttpRequestParser()) .clientResponseParser(new CustomHttpResponseParser()) .serverRequestParser(new CustomHttpRequestParser()) .serverResponseParser(new CustomHttpResponseParser()).build(); } @Bean ZipkinTracingFilter scbZipkinTracingFilter() { return new ZipkinTracingFilter(); } public static String createRequestPath(Invocation invocation) { URIEndpointObject address = (URIEndpointObject) invocation.getEndpoint().getAddress(); String urlPrefix = address.getFirst(DefinitionConst.URL_PREFIX); RestOperationMeta swaggerRestOperation = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); String path = (String) invocation.getHandlerContext().get(RestConst.REST_CLIENT_REQUEST_PATH); if (path == null) { try { path = swaggerRestOperation.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); } catch (Exception e) { path = invocation.getOperationMeta().getOperationPath(); } } if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { return path; } return urlPrefix + path; } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.eventbus.Subscribe; import brave.Span; import brave.http.HttpClientHandler; import brave.http.HttpClientRequest; import brave.http.HttpClientResponse; import brave.http.HttpServerHandler; import brave.http.HttpServerRequest; import brave.http.HttpServerResponse; import brave.http.HttpTracing; public class ZipkinTracingFilter { public static final String CONTEXT_TRACE_REQUEST = "x-trace-request"; public static final String CONTEXT_TRACE_HANDLER = "x-trace-handler"; public static final String CONTEXT_TRACE_SPAN = "x-trace-span"; public static final String CONTEXT_TRACE_EDGE_REQUEST = "x-trace-edge-request"; public static final String CONTEXT_TRACE_EDGE_HANDLER = "x-trace-edge-handler"; public static final String CONTEXT_TRACE_EDGE_SPAN = "x-trace-edge-span"; private HttpTracing httpTracing; public ZipkinTracingFilter() { EventManager.register(this); } @Autowired public void setHttpTracing(HttpTracing httpTracing) { this.httpTracing = httpTracing; } @Subscribe public void onInvocationStartEvent(InvocationStartEvent event) { Invocation invocation = event.getInvocation(); if (invocation.isProducer()) { HttpServerHandler handler = HttpServerHandler.create(httpTracing); HttpServeRequestWrapper request = new HttpServeRequestWrapper(invocation); Span span = handler.handleReceive(request); invocation.addLocalContext(CONTEXT_TRACE_SPAN, span); invocation.addLocalContext(CONTEXT_TRACE_HANDLER, handler); invocation.addLocalContext(CONTEXT_TRACE_REQUEST, request); } else if (invocation.isConsumer()) { Span parentSpan = invocation.getLocalContext(CONTEXT_TRACE_SPAN); HttpClientHandler handler = HttpClientHandler.create(httpTracing); HttpClientRequestWrapper request = new HttpClientRequestWrapper(invocation); Span span = handler.handleSendWithParent(request, parentSpan == null ? null : parentSpan.context()); invocation.addLocalContext(CONTEXT_TRACE_HANDLER, handler); invocation.addLocalContext(CONTEXT_TRACE_REQUEST, request); invocation.addLocalContext(CONTEXT_TRACE_SPAN, span); } else { // edge as server HttpServerHandler serverHandler = HttpServerHandler.create(httpTracing); HttpServeRequestWrapper serverRequest = new HttpServeRequestWrapper(invocation); Span serverSpan = serverHandler.handleReceive(serverRequest); invocation.addLocalContext(CONTEXT_TRACE_EDGE_SPAN, serverSpan); invocation.addLocalContext(CONTEXT_TRACE_EDGE_HANDLER, serverHandler); invocation.addLocalContext(CONTEXT_TRACE_EDGE_REQUEST, serverRequest); // edge as client HttpClientHandler clientHandler = HttpClientHandler.create(httpTracing); HttpClientRequestWrapper clientRequest = new HttpClientRequestWrapper(invocation); Span clientSpan = clientHandler.handleSendWithParent(clientRequest, serverSpan.context()); invocation.addLocalContext(CONTEXT_TRACE_HANDLER, clientHandler); invocation.addLocalContext(CONTEXT_TRACE_REQUEST, clientRequest); invocation.addLocalContext(CONTEXT_TRACE_SPAN, clientSpan); } } @Subscribe public void onInvocationFinishEvent(InvocationFinishEvent event) { Invocation invocation = event.getInvocation(); if (invocation.isProducer()) { HttpServerHandler handler = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); Span span = invocation.getLocalContext(CONTEXT_TRACE_SPAN); handler.handleSend(new HttpServerResponseWrapper(invocation, event.getResponse(), invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), span); } else if (invocation.isConsumer()) { HttpClientHandler handler = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); Span span = invocation.getLocalContext(CONTEXT_TRACE_SPAN); handler.handleReceive(new HttpClientResponseWrapper(invocation, event.getResponse(), invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), span); } else { // edge as client HttpClientHandler clientHandler = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); Span clientSpan = invocation.getLocalContext(CONTEXT_TRACE_SPAN); clientHandler.handleReceive(new HttpClientResponseWrapper(invocation, event.getResponse(), invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), clientSpan); // edge as server HttpServerHandler serverHandler = invocation.getLocalContext(CONTEXT_TRACE_EDGE_HANDLER); Span serverSpan = invocation.getLocalContext(CONTEXT_TRACE_EDGE_SPAN); serverHandler.handleSend(new HttpServerResponseWrapper(invocation, event.getResponse(), invocation.getLocalContext(CONTEXT_TRACE_EDGE_REQUEST)), serverSpan); } } } ================================================ FILE: handlers/handler-tracing-zipkin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.tracing.zipkin.TracingConfiguration ================================================ FILE: handlers/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 handlers Java Chassis::Handlers pom handler-tracing-zipkin handler-flowcontrol-qps handler-loadbalance handler-fault-injection handler-publickey-auth handler-router handler-governance ================================================ FILE: huawei-cloud/darklaunch/pom.xml ================================================ huawei-cloud org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 darklaunch Java Chassis::darklaunch org.apache.servicecomb foundation-registry org.apache.servicecomb registry-service-center org.apache.servicecomb handler-loadbalance ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.darklaunch.oper.ConditionFactory; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DarklaunchRule { private static final Logger LOG = LoggerFactory.getLogger(DarklaunchRule.class); public static final String PROP_VERSION = "version"; public static final String PROP_TAG = "tag"; public static final String PROP_PERCENT = "rate"; private PolicyType policyType; private final List ruleItems = new ArrayList<>(); public static DarklaunchRule parse(String ruleStr) { if (StringUtils.isEmpty(ruleStr)) { return null; } try { DarklaunchRuleJson ruleJson = JsonUtils .readValue(ruleStr.getBytes(StandardCharsets.UTF_8), DarklaunchRuleJson.class); DarklaunchRule rule = new DarklaunchRule(ruleJson.getPolicyType()); for (DarklaunchRuleItemJson itemJson : ruleJson.getRuleItems()) { DarklaunchRuleItem item = new DarklaunchRuleItem(itemJson.getGroupName()); item.setGroupCondition( ConditionFactory.buildCondition(itemJson.getGroupCondition(), itemJson.isCaseInsensitive()) ); if (rule.getPolicyType().equals(PolicyType.RULE)) { item.setPolicyCondition( ConditionFactory.buildCondition(itemJson.getPolicyCondition(), itemJson.isCaseInsensitive())); } else { item.setPolicyCondition(ConditionFactory.buildRateCondition(itemJson.getPolicyCondition())); } rule.addRuleItem(item); } return rule; } catch (Exception e) { LOG.warn("Invalid configuration: rule={},message={}", ruleStr, e.getMessage()); } return null; } public DarklaunchRule(PolicyType policyType) { this.policyType = policyType; } public PolicyType getPolicyType() { return policyType; } public void setPolicyType(PolicyType policyType) { this.policyType = policyType; } public List getRuleItems() { return ruleItems; } public void addRuleItem(DarklaunchRuleItem ruleItem) { this.ruleItems.add(ruleItem); } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItem.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.darklaunch.oper.Condition; import org.apache.servicecomb.loadbalance.ServiceCombServer; public class DarklaunchRuleItem { private String groupName; private Condition groupCondition; private Condition policyCondition; private final List servers = new ArrayList<>(); public DarklaunchRuleItem(String groupName) { this.groupName = groupName; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public Condition getGroupCondition() { return groupCondition; } public void setGroupCondition(Condition groupCondition) { this.groupCondition = groupCondition; } public Condition getPolicyCondition() { return policyCondition; } public void setPolicyCondition(Condition policyCondition) { this.policyCondition = policyCondition; } public List getServers() { return this.servers; } public void addServer(ServiceCombServer server) { this.servers.add(server); } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItemJson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class DarklaunchRuleItemJson { private String groupName; private String groupCondition; private String policyCondition; private boolean caseInsensitive; public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getGroupCondition() { return groupCondition; } public void setGroupCondition(String groupCondition) { this.groupCondition = groupCondition; } public String getPolicyCondition() { return policyCondition; } public void setPolicyCondition(String policyCondition) { this.policyCondition = policyCondition; } public boolean isCaseInsensitive() { return caseInsensitive; } public void setCaseInsensitive(boolean caseInsensitive) { this.caseInsensitive = caseInsensitive; } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleJson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class DarklaunchRuleJson { private PolicyType policyType; private List ruleItems = new ArrayList<>(); public PolicyType getPolicyType() { return policyType; } public void setPolicyType(PolicyType policyType) { this.policyType = policyType; } public List getRuleItems() { return ruleItems; } public void setRuleItems(List ruleItems) { this.ruleItems = ruleItems; } public void addRuleItem(DarklaunchRuleItemJson ruleItem) { this.ruleItems.add(ruleItem); } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchServerListFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.loadbalance.ServerListFilterExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; public class DarklaunchServerListFilter implements ServerListFilterExt { private static final String POLICY_SERVICE_COMB = "servicecomb.darklaunch.policy.%s"; private static final int HUNDRED = 100; private final Random random = new Random(); public DarklaunchServerListFilter() { } @Override public int getOrder() { return HUNDRED; } @Override public boolean enabled() { return true; } @Override public List getFilteredListOfServers(List serverList, Invocation invocation) { DarklaunchRule rule = DarklaunchRule.parse(LegacyPropertyFactory .getStringProperty(String.format(POLICY_SERVICE_COMB, invocation.getMicroserviceName()))); if (rule == null) { return serverList; } List defaultGroup = new ArrayList<>(); divideServerGroup(serverList, rule, defaultGroup); if (rule.getPolicyType() == PolicyType.RULE) { for (DarklaunchRuleItem item : rule.getRuleItems()) { List ruleServers = getRuleServers(invocation, item, defaultGroup); if (ruleServers != null) { return ruleServers; } } } else { int rate = random.nextInt(HUNDRED); for (DarklaunchRuleItem item : rule.getRuleItems()) { item.getPolicyCondition().setActual(DarklaunchRule.PROP_PERCENT, rate); if (item.getPolicyCondition().match()) { if (item.getServers().isEmpty()) { return defaultGroup; } return item.getServers(); } rate = rate - Integer.parseInt(item.getPolicyCondition().expected()); } } return defaultGroup; } private List getRuleServers(Invocation invocation, DarklaunchRuleItem item, List defaultGroup) { invocation.getSwaggerArguments().forEach((k, v) -> item.getPolicyCondition().setActual(k, v)); for (String key : invocation.getContext().keySet()) { item.getPolicyCondition().setActual(key, invocation.getContext(key)); } if (item.getPolicyCondition().match()) { if (item.getServers().isEmpty()) { return defaultGroup; } return item.getServers(); } return null; } private void divideServerGroup(List serverList, DarklaunchRule rule, List defaultGroup) { for (ServiceCombServer server : serverList) { boolean hasGroup = false; for (DarklaunchRuleItem item : rule.getRuleItems()) { item.getGroupCondition() .setActual(DarklaunchRule.PROP_VERSION, server.getInstance().getVersion()); if (item.getGroupCondition().match()) { item.addServer(server); hasGroup = true; } } if (!hasGroup) { defaultGroup.add(server); } } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/PolicyType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch; public enum PolicyType { RULE, RATE } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AbstractCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public abstract class AbstractCondition implements Condition { private final String key; private final String expected; private Object actual; private SupportedType type = SupportedType.UNKNOWN; public AbstractCondition(String key, String expected) { assertValueNotNull(key, expected); this.key = key; this.expected = expected; } @Override public String key() { return this.key; } @Override public String expected() { return this.expected; } @Override public void setActual(String key, Object actual) { assertValueNotNull(key, ""); if (this.key.equals(key)) { if (actual instanceof String) { this.type = SupportedType.STRING; } else if (actual instanceof Number) { this.type = SupportedType.NUMBER; } this.actual = actual; } } protected void assertValueNotNull(String key, Object value) { if (key == null) { throw new IllegalArgumentException("Key can not be null."); } if (value == null) { throw new IllegalArgumentException("Argument can not be null. key = " + key); } } public static int compareNum(Object num, String anotherNum) { try { if (num instanceof Integer) { return Integer.compare((Integer) num, Integer.parseInt(anotherNum)); } if (num instanceof Long) { return Long.compare((Long) num, Long.parseLong(anotherNum)); } if (num instanceof Double) { return Double.compare((Double) num, Double.parseDouble(anotherNum)); } if (num instanceof Float) { return Float.compare((Float) num, Float.parseFloat(anotherNum)); } } catch (NumberFormatException e) { return 1; } return 1; } public Object getActual() { return actual; } public SupportedType getType() { return type; } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AndCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class AndCondition extends AbstractCondition { private final Condition[] conditions; public AndCondition(Condition... conditions) { super("and", "and"); this.conditions = conditions; } @Override public void setActual(String key, Object actual) { for (Condition condition : this.conditions) { condition.setActual(key, actual); } } @Override public boolean match() { for (Condition condition : this.conditions) { if (!condition.match()) { return false; } } return true; } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/CaseInsensitiveCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class CaseInsensitiveCondition extends AbstractCondition { private final Condition condition; public CaseInsensitiveCondition(Condition condition) { super(condition.key(), condition.expected()); this.condition = condition; } @Override public void setActual(String key, Object actual) { if (null == actual) { condition.setActual(key, null); return; } actual = actual.toString().toLowerCase(); condition.setActual(key, actual); } @Override public boolean match() { return condition.match(); } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/Condition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public interface Condition { String key(); String expected(); void setActual(String key, Object actual); boolean match(); } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/ConditionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.darklaunch.DarklaunchRule; public class ConditionFactory { public static final String OP_AND = "&&"; public static final String OP_OR = "||"; public static final String OP_OR_ESCAPE = "\\|\\|"; private static final String[] OP_LIST = {">=", "<=", "!=", "=", ">", "<", "~"}; public static final String SEP_COLON = ","; private static String[] split2Part(String str, String sep) { int index = str.indexOf(sep); if (index > 0) { return new String[] { str.substring(0, index), str.substring(index + sep.length()) }; } else { return new String[] {str}; } } private static String[] split(String str, String sep) { return Arrays.stream(str.split(sep)) .filter(s -> !StringUtils.isEmpty(s)).toArray(String[]::new); } public static Condition buildRateCondition(String strCondition) { return new LessCondition(DarklaunchRule.PROP_PERCENT, strCondition); } public static Condition buildCondition(String strCondition, boolean caseInsensitive) { if (strCondition.contains(OP_AND)) { String[] rules = split(strCondition, OP_AND); Condition[] conditions = new Condition[rules.length]; for (int i = 0; i < conditions.length; i++) { conditions[i] = buildGroupConditionItem(rules[i], caseInsensitive); } return new AndCondition(conditions); } else if (strCondition.contains(OP_OR)) { String[] rules = split(strCondition, OP_OR_ESCAPE); Condition[] conditions = new Condition[rules.length]; for (int i = 0; i < conditions.length; i++) { conditions[i] = buildGroupConditionItem(rules[i], caseInsensitive); } return new OrCondition(conditions); } else { return buildGroupConditionItem(strCondition, caseInsensitive); } } private static Condition buildGroupConditionItem(String groupCondition, boolean caseInsensitive) { for (int index = 0; index < OP_LIST.length; index++) { if (groupCondition.contains(OP_LIST[index])) { String[] pairs = split2Part(groupCondition, OP_LIST[index]); if (pairs[1].contains(SEP_COLON)) { String[] values = split(pairs[1], SEP_COLON); Condition[] conditions = new Condition[values.length]; for (int i = 0; i < values.length; i++) { conditions[i] = buildCondition(index, pairs[0], values[i], caseInsensitive); } return new OrCondition(conditions); } else { return buildCondition(index, pairs[0], pairs[1], caseInsensitive); } } } throw new IllegalArgumentException(groupCondition); } private static Condition buildCondition(int index, String key, String value, boolean caseInsensitive) { value = caseInsensitive ? value.toLowerCase() : value; Condition condition = buildCondition(index, key, value); if (caseInsensitive) { return new CaseInsensitiveCondition(condition); } return condition; } private static Condition buildCondition(int index, String key, String value) { switch (index) { case 0: return new GreaterOrEqualCondition(key, value); case 1: return new LessOrEqualCondition(key, value); case 2: return new NotEqualCondition(key, value); case 3: return new EqualCondition(key, value); case 4: return new GreaterCondition(key, value); case 5: return new LessCondition(key, value); default: return new LikeCondition(key, value); } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/EqualCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class EqualCondition extends AbstractCondition { public EqualCondition(String key, String expected) { super(key, expected); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.NUMBER) { return compareNum(this.getActual(), this.expected()) == 0; } else if (type == SupportedType.STRING) { return ((String) this.getActual()).compareTo(this.expected()) == 0; } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class GreaterCondition extends AbstractCondition { public GreaterCondition(String key, String expected) { super(key, expected); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.NUMBER) { return compareNum(this.getActual(), this.expected()) > 0; } else if (type == SupportedType.STRING) { return ((String) this.getActual()).compareTo(this.expected()) > 0; } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterOrEqualCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class GreaterOrEqualCondition extends AbstractCondition { public GreaterOrEqualCondition(String key, String expected) { super(key, expected); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.NUMBER) { return compareNum(this.getActual(), this.expected()) >= 0; } else if (type == SupportedType.STRING) { return ((String) this.getActual()).compareTo(this.expected()) >= 0; } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class LessCondition extends AbstractCondition { public LessCondition(String key, String expected) { super(key, expected); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.NUMBER) { return compareNum(this.getActual(), this.expected()) < 0; } else if (type == SupportedType.STRING) { return ((String) this.getActual()).compareTo(this.expected()) < 0; } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessOrEqualCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class LessOrEqualCondition extends AbstractCondition { public LessOrEqualCondition(String key, String expected) { super(key, expected); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.NUMBER) { return compareNum(this.getActual(), this.expected()) <= 0; } else if (type == SupportedType.STRING) { return ((String) this.getActual()).compareTo(this.expected()) <= 0; } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LikeCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; import java.util.regex.Pattern; public class LikeCondition extends AbstractCondition { private final Pattern pattern; public LikeCondition(String key, String expected) { super(key, expected); char[] cs = expected.toCharArray(); StringBuilder regExp = new StringBuilder(); int lastPos = 0; for (int i = 0; i < cs.length; i++) { if ((cs[i]) == '*') { regExp.append(Pattern.quote(new String(cs, lastPos, i - lastPos))); regExp.append(".*"); lastPos = i + 1; } else if (cs[i] == '?') { regExp.append(Pattern.quote(new String(cs, lastPos, i - lastPos))); regExp.append("."); lastPos = i = 1; } } regExp.append(Pattern.quote(new String(cs, lastPos, cs.length - lastPos))); pattern = Pattern.compile(regExp.toString()); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.STRING) { return this.pattern.matcher((String) this.getActual()).matches(); } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/NotEqualCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class NotEqualCondition extends AbstractCondition { public NotEqualCondition(String key, String expected) { super(key, expected); } @Override public boolean match() { SupportedType type = this.getType(); if (type == SupportedType.NUMBER) { return compareNum(this.getActual(), this.expected()) != 0; } else if (type == SupportedType.STRING) { return ((String) this.getActual()).compareTo(this.expected()) != 0; } else { return false; } } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/OrCondition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public class OrCondition extends AbstractCondition { private final Condition[] conditions; public OrCondition(Condition... conditions) { super("or", "or"); this.conditions = conditions; } @Override public void setActual(String key, Object actual) { for (Condition condition : this.conditions) { condition.setActual(key, actual); } } @Override public boolean match() { for (Condition condition : this.conditions) { if (condition.match()) { return true; } } return false; } } ================================================ FILE: huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/SupportedType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.darklaunch.oper; public enum SupportedType { STRING, NUMBER, UNKNOWN } ================================================ FILE: huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.darklaunch.DarklaunchServerListFilter ================================================ FILE: huawei-cloud/dashboard/pom.xml ================================================ huawei-cloud org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 dashboard org.apache.servicecomb foundation-common org.apache.servicecomb java-chassis-core org.apache.servicecomb registry-service-center org.apache.servicecomb dashboard-client org.apache.servicecomb metrics-core provided ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class DashboardConfiguration { @Bean public MonitorInformationCollector scbMonitorInformationCollector() { return new MonitorInformationCollector(); } @Bean public MonitorConstant scbMonitorConstant(Environment environment) { return new MonitorConstant(environment); } @Bean public MonitorBootListener scbMonitorBootListener() { return new MonitorBootListener(); } @Bean public DataFactory scbDataFactory() { return new DataFactory(); } @Bean public MetricsMonitorDataProvider scbMetricsMonitorDataProvider() { return new MetricsMonitorDataProvider(); } @Bean public DefaultMonitorDataPublisher scbDefaultMonitorDataPublisher() { return new DefaultMonitorDataPublisher(); } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DataFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataPublisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import io.netty.util.concurrent.DefaultThreadFactory; public class DataFactory { private static final Logger LOGGER = LoggerFactory.getLogger(DataFactory.class); private static final int CORE_SIZE = 1; private boolean hasStart = false; @Autowired private List dataProviders; @Autowired private MonitorDataPublisher publisher; @Autowired private MonitorConstant monitorConstant; private ScheduledExecutorService executorService = null; public DataFactory() { ThreadFactory threadFactory = new DefaultThreadFactory("monitor-datafactory"); executorService = Executors.newScheduledThreadPool(CORE_SIZE, threadFactory); } public void setMonitorDataProviders(List dataProviders) { this.dataProviders = dataProviders; } public void setMonitorDataPublisher(MonitorDataPublisher publisher) { this.publisher = publisher; } void start() { if (!hasStart) { publisher.init(); StringBuilder sb = new StringBuilder(); sb.append("Monitor data sender started. Configured data providers is {"); for (MonitorDataProvider provider : dataProviders) { sb.append(provider.getClass().getName()); sb.append(","); } sb.append("}"); LOGGER.info(sb.toString()); executorService.scheduleWithFixedDelay(() -> { try { sendData(); } catch (Throwable e) { LOGGER.error("send monitor data error.", e); } }, monitorConstant.getInterval(), monitorConstant.getInterval(), TimeUnit.MILLISECONDS); hasStart = true; } } void sendData() { for (MonitorDataProvider provider : this.dataProviders) { if (provider.enabled()) { this.publisher.publish(provider); } } } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.dashboard.client.DashboardAddressManager; import org.apache.servicecomb.dashboard.client.DashboardClient; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataPublisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class DefaultMonitorDataPublisher implements MonitorDataPublisher { private static final String SSL_KEY = "mc.consumer"; private DashboardClient dashboardClient; private MonitorConstant monitorConstant; private Environment environment; @Autowired public void setMonitorConstant(MonitorConstant monitorConstant) { this.monitorConstant = monitorConstant; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void init() { DashboardAddressManager addressManager = createDashboardAddressManager(); RequestConfig.Builder requestBuilder = HttpTransportFactory.defaultRequestConfig(); requestBuilder.setConnectionRequestTimeout(1000); requestBuilder.setSocketTimeout(10000); HttpTransport httpTransport = createHttpTransport(addressManager, requestBuilder.build(), environment); dashboardClient = new DashboardClient(addressManager, httpTransport); } @SuppressWarnings("unchecked") private DashboardAddressManager createDashboardAddressManager() { List addresses = ConfigUtil.parseArrayValue( environment.getProperty(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE, "")); if (addresses.isEmpty()) { throw new IllegalStateException("dashboard address is not configured."); } String region = environment.getProperty("servicecomb.datacenter.region"); String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); return new DashboardAddressManager(addresses, EventManager.getEventBus(), region, availableZone); } private HttpTransport createHttpTransport(DashboardAddressManager addressManager, RequestConfig requestConfig, Environment environment) { List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); if (monitorConstant.isProxyEnable()) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(requestConfig); HttpHost proxy = new HttpHost(monitorConstant.getProxyHost(), monitorConstant.getProxyPort(), "http"); // now only support http proxy httpClientBuilder.setProxy(proxy); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(monitorConstant.getProxyUsername(), monitorConstant.getProxyPasswd())); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return HttpTransportFactory .createHttpTransport( TransportUtils .createSSLProperties(addressManager.sslEnabled(), environment, SSL_KEY), getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); } return HttpTransportFactory .createHttpTransport( TransportUtils .createSSLProperties(monitorConstant.sslEnabled(), environment, SSL_KEY), getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); } private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } @Override public void publish(MonitorDataProvider provider) { dashboardClient.sendData(provider.getURL(), provider.getData()); } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.dashboard.client.model.InterfaceInfo; import org.apache.servicecomb.dashboard.client.model.MonitorData; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.metrics.PolledEvent; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; import org.apache.servicecomb.metrics.core.publish.PublishModelFactory; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; import org.apache.servicecomb.registry.sc.SCRegistration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; import io.micrometer.core.instrument.Meter; /** * Monitor data based on metrics-core module. */ public class MetricsMonitorDataProvider implements MonitorDataProvider { public static final String CODE_SUCCESS = "2[0-9]{2}"; public static final String CODE_TIMEOUT = "408"; public static final String NAME_PROVIDER = "Provider."; public static final String NAME_CONSUMER = "Consumer."; private volatile List meters = null; private SCRegistration scRegistration; private Environment environment; private MonitorConstant monitorConstant; public MetricsMonitorDataProvider() { EventManager.register(this); } @Autowired public void setSCRegistration(SCRegistration scRegistration) { this.scRegistration = scRegistration; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setMonitorConstant(MonitorConstant monitorConstant) { this.monitorConstant = monitorConstant; } @Override public boolean enabled() { return environment.getProperty("servicecomb.monitor.provider.metrics.enabled", boolean.class, true); } @Override public String getURL() { return String.format(monitorConstant.getMonitorUri(), scRegistration.getMicroserviceInstance().getServiceName()); } @Override public void extractServiceInfo(MonitorData monitorData) { monitorData.setAppId(scRegistration.getMicroserviceInstance().getApplication()); monitorData.setName(scRegistration.getMicroserviceInstance().getServiceName()); monitorData.setVersion(scRegistration.getMicroserviceInstance().getVersion()); monitorData.setServiceId(scRegistration.getMicroserviceInstance().getBackendMicroservice().getServiceId()); monitorData.setInstance(scRegistration.getMicroserviceInstance().getBackendMicroserviceInstance().getHostName()); monitorData.setInstanceId(scRegistration.getMicroserviceInstance().getInstanceId()); monitorData.setEnvironment(scRegistration.getMicroserviceInstance().getEnvironment()); } @Override public void extractInterfaceInfo(MonitorData monitorData) { if (meters == null) { return; } PublishModelFactory factory = new PublishModelFactory(meters); DefaultPublishModel model = factory.createDefaultPublishModel(); Map combinedResults = new HashMap<>(); extractProviderInfo(model, combinedResults); extractConsumerInfo(model, combinedResults); extractEdgeInfo(model, combinedResults); combinedResults.forEach((k, v) -> { v.setFailureRate(v.getTotal() == 0 ? 0 : v.getFailure() / (double) v.getTotal()); monitorData.addInterfaceInfo(v); }); } private void extractProviderInfo(DefaultPublishModel model, Map combinedResults) { OperationPerfGroups producerPerf = model.getProducer().getOperationPerfGroups(); if (producerPerf == null) { return; } for (Map statusMap : producerPerf.getGroups().values()) { for (OperationPerfGroup perfGroup : statusMap.values()) { for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); String name = NAME_PROVIDER + operationPerf.getOperation(); InterfaceInfo interfaceInfo = combinedResults.computeIfAbsent(name, k -> { InterfaceInfo obj = new InterfaceInfo(); obj.setName(name); return obj; }); // dashboard calculates the latest 10 seconds, different with metrics cycle. interfaceInfo.setTotal( doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); if (perfGroup.getStatus().matches(CODE_SUCCESS)) { interfaceInfo.setQps(stageTotal.getTotalRequests() / 10); interfaceInfo.setLatency(doubleToInt(stageTotal.calcMsLatency())); } else { interfaceInfo.setFailure( doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); if (perfGroup.getStatus().equals(CODE_TIMEOUT)) { interfaceInfo.setCountTimeout( doubleToInt(interfaceInfo.getCountTimeout() + stageTotal.getTotalRequests())); } } } } } } private void extractEdgeInfo(DefaultPublishModel model, Map combinedResults) { OperationPerfGroups edgePerf = model.getEdge().getOperationPerfGroups(); if (edgePerf == null) { return; } for (Map statusMap : edgePerf.getGroups().values()) { for (OperationPerfGroup perfGroup : statusMap.values()) { for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); String name = NAME_CONSUMER + operationPerf.getOperation(); InterfaceInfo interfaceInfo = combinedResults.computeIfAbsent(name, k -> { InterfaceInfo obj = new InterfaceInfo(); obj.setName(name); return obj; }); // dashboard calculates the latest 10 seconds, different with metrics cycle. interfaceInfo.setTotal( doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); if (perfGroup.getStatus().matches(CODE_SUCCESS)) { interfaceInfo.setQps(stageTotal.getTotalRequests() / 10); interfaceInfo.setLatency(doubleToInt(stageTotal.calcMsLatency())); } else { interfaceInfo.setFailure( doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); if (perfGroup.getStatus().equals(CODE_TIMEOUT)) { interfaceInfo.setCountTimeout( doubleToInt( interfaceInfo.getCountTimeout() + stageTotal.getTotalRequests())); } } } } } } private void extractConsumerInfo(DefaultPublishModel model, Map combinedResults) { OperationPerfGroups consumerPerf = model.getConsumer().getOperationPerfGroups(); if (consumerPerf == null) { return; } for (Map statusMap : consumerPerf.getGroups().values()) { for (OperationPerfGroup perfGroup : statusMap.values()) { for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); String name = NAME_CONSUMER + operationPerf.getOperation(); InterfaceInfo interfaceInfo = combinedResults.computeIfAbsent(name, k -> { InterfaceInfo obj = new InterfaceInfo(); obj.setName(name); return obj; }); // dashboard calculates the latest 10 seconds, different with metrics cycle. interfaceInfo.setTotal( doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); if (perfGroup.getStatus().matches(CODE_SUCCESS)) { interfaceInfo.setQps(stageTotal.getTotalRequests() / 10); interfaceInfo.setLatency(doubleToInt(stageTotal.calcMsLatency())); } else { interfaceInfo.setFailure( doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); if (perfGroup.getStatus().equals(CODE_TIMEOUT)) { interfaceInfo.setCountTimeout( doubleToInt( interfaceInfo.getCountTimeout() + stageTotal.getTotalRequests())); } } } } } } private int doubleToInt(Double d) { return d.intValue(); } @Subscribe public void onPolledEvent(PolledEvent event) { this.meters = event.getMeters(); } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.springframework.beans.factory.annotation.Autowired; public class MonitorBootListener implements BootListener { @Autowired private DataFactory dataFactory; @Autowired private MonitorConstant monitorConstant; @Override public void onBootEvent(BootEvent event) { if (!monitorConstant.isMonitorEnabled()) { return; } if (EventType.AFTER_REGISTRY == event.getEventType()) { dataFactory.start(); } } public DataFactory getDataFactory() { return dataFactory; } public void setDataFactory(DataFactory dataFactory) { this.dataFactory = dataFactory; } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorInformationCollector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.bootup.BootUpInformationCollector; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class MonitorInformationCollector implements BootUpInformationCollector { private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public String collect() { return "monitor center: " + ConfigUtil.parseArrayValue( environment.getProperty(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE, "")); } @Override public int getOrder() { return 100; } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/TransportUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.springframework.core.env.Environment; public class TransportUtils { public static SSLProperties createSSLProperties(boolean sslEnabled, Environment environment, String tag) { SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(sslEnabled); if (!sslEnabled) { return sslProperties; } SSLOption option = new SSLOption(); option.setEngine(getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + tag + ".engine", "ssl.engine")); option.setProtocols( getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + tag + ".protocols", "ssl.protocols")); option.setCiphers( getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers")); option.setAuthPeer( getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer")); option.setCheckCNHost( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + tag + ".checkCN.host", "ssl.checkCN.host")); option.setCheckCNWhite( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + tag + ".checkCN.white", "ssl.checkCN.white")); option.setCheckCNWhiteFile(getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); option.setAllowRenegotiate(getBooleanProperty(environment, DEFAULT_OPTION.isAllowRenegotiate(), "ssl." + tag + ".allowRenegotiate", "ssl.allowRenegotiate")); option.setStorePath( getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + tag + ".storePath", "ssl.storePath")); option.setClientAuth( getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + tag + ".clientAuth", "ssl.clientAuth")); option.setTrustStore( getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + tag + ".trustStore", "ssl.trustStore")); option.setTrustStoreType(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + tag + ".trustStoreType", "ssl.trustStoreType")); option.setTrustStoreValue(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + tag + ".trustStoreValue", "ssl.trustStoreValue")); option.setKeyStore( getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore")); option.setKeyStoreType( getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + tag + ".keyStoreType", "ssl.keyStoreType")); option.setKeyStoreValue(getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + tag + ".keyStoreValue", "ssl.keyStoreValue")); option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl")); option.setSslCustomClass( getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass")); sslProperties.setSslOption(option); sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); return sslProperties; } private static String getStringProperty(Environment environment, String defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key); } } return defaultValue; } private static boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key, boolean.class, false); } } return defaultValue; } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/CPUMonitorCalc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor.data; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.lang.management.ThreadMXBean; public class CPUMonitorCalc { private static final int PERCENTAGE = 100; private static final CPUMonitorCalc instance = new CPUMonitorCalc(); private final OperatingSystemMXBean osMxBean; private final ThreadMXBean threadMXBean; private long preTime = System.nanoTime(); private long preUsedTime = 0; private CPUMonitorCalc() { osMxBean = ManagementFactory.getOperatingSystemMXBean(); threadMXBean = ManagementFactory.getThreadMXBean(); } public static CPUMonitorCalc getInstance() { return instance; } public double getProcessCpu() { long totalTime = 0; for (long id : threadMXBean.getAllThreadIds()) { totalTime += threadMXBean.getThreadCpuTime(id); } long curtime = System.nanoTime(); long usedTime = totalTime - preUsedTime; long totalPassedTime = curtime - preTime; preTime = curtime; preUsedTime = totalTime; return (((double) usedTime) / totalPassedTime / osMxBean.getAvailableProcessors()) * PERCENTAGE; } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/MonitorConstant.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor.data; import org.springframework.core.env.Environment; public class MonitorConstant { public static final String SYSTEM_KEY_DASHBOARD_SERVICE = "DashboardService"; public static final String MONITOR_URI = "servicecomb.monitor.client.serverUri"; public static final String PROXY_ENABLE = "servicecomb.proxy.enable"; public static final String PROXY_HOST = "servicecomb.proxy.host"; public static final String PROXY_PORT = "servicecomb.proxy.port"; public static final String PROXY_USERNAME = "servicecomb.proxy.username"; public static final String PROXY_PASSWD = "servicecomb.proxy.passwd"; public static final int MIN_INTERVAL_MILLISECONDS = 5000; public static final int DEFAULT_TIMEOUT = 5000; public static final int DEFAULT_INTERVAL = 10000; private final String DOMAIN_NAME; private final String CURRENT_VERSION; private final String VERSION_V1; private final String PREFIX_V2; private final String monitorsUri; private Environment environment; public MonitorConstant(Environment environment) { this.environment = environment; this.VERSION_V1 = "v1"; this.DOMAIN_NAME = getDomainName(); this.PREFIX_V2 = String.format("/v2/%s/csemonitor", DOMAIN_NAME); this.CURRENT_VERSION = getApiVersion(); if (VERSION_V1.equals(CURRENT_VERSION)) { monitorsUri = "/csemonitor/v1/metric?service=%s"; } else { monitorsUri = PREFIX_V2 + "/metric?service=%s"; } } public String getMonitorUri() { return this.monitorsUri; } public String getDomainName() { return environment.getProperty("servicecomb.config.client.domainName", "default"); } public String getApiVersion() { return environment.getProperty("servicecomb.monitor.client.api.version", "v2"); } public String getServerUrl() { return environment.getProperty("servicecomb.monitor.client.serverUri"); } public boolean sslEnabled() { return environment.getProperty("servicecomb.monitor.client.sslEnabled", boolean.class, true); } public boolean isMonitorEnabled() { return environment.getProperty("servicecomb.monitor.client.enabled", boolean.class, false); } public int getConnectionTimeout() { return environment.getProperty("servicecomb.monitor.client.timeout", int.class, DEFAULT_TIMEOUT); } public int getInterval() { return Math.max(environment.getProperty("servicecomb.monitor.client.interval", int.class, DEFAULT_INTERVAL), MIN_INTERVAL_MILLISECONDS); } public Boolean isProxyEnable() { return Boolean.parseBoolean(getProperty("false", PROXY_ENABLE)); } public String getProxyHost() { return getProperty("127.0.0.1", PROXY_HOST); } public int getProxyPort() { return Integer.parseInt(getProperty("8080", PROXY_PORT)); } public String getProxyUsername() { return getProperty(null, PROXY_USERNAME); } public String getProxyPasswd() { return getProperty(null, PROXY_PASSWD); } private String getProperty(String defaultValue, String... keys) { String property = null; for (String key : keys) { property = environment.getProperty(key); if (property != null) { break; } } if (property != null) { return property; } return defaultValue; } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor.model; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.lang.management.ThreadMXBean; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.dashboard.client.model.MonitorData; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.CPUMonitorCalc; public interface MonitorDataProvider { boolean enabled(); String getURL(); void extractServiceInfo(MonitorData monitorData); default void exactProcessInfo(MonitorData monitorData) { MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage memoryHeapUsage = memoryMXBean.getHeapMemoryUsage(); MemoryUsage memoryNonHeapUsage = memoryMXBean.getNonHeapMemoryUsage(); ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); int threadCount = threadMXBean.getThreadCount(); OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); double cpu = operatingSystemMXBean.getSystemLoadAverage(); monitorData.setCpu(CPUMonitorCalc.getInstance().getProcessCpu()); monitorData.setLoadAverage(cpu); monitorData.setThreadCount(threadCount); monitorData.setUptime(runtimeMXBean.getUptime()); Map memoryInfo = new HashMap<>(); memoryInfo.put("heapInit", memoryHeapUsage.getInit()); memoryInfo.put("headMax", memoryHeapUsage.getMax()); memoryInfo.put("heapCommit", memoryHeapUsage.getCommitted()); memoryInfo.put("heapUsed", memoryHeapUsage.getUsed()); memoryInfo.put("nonHeapInit", memoryNonHeapUsage.getInit()); memoryInfo.put("nonHeapCommit", memoryNonHeapUsage.getCommitted()); memoryInfo.put("nonHeapUsed", memoryNonHeapUsage.getUsed()); monitorData.setMemory(memoryInfo); } void extractInterfaceInfo(MonitorData monitorData); default MonitorData getData() { MonitorData monitorData = new MonitorData(); extractServiceInfo(monitorData); exactProcessInfo(monitorData); extractInterfaceInfo(monitorData); return monitorData; } } ================================================ FILE: huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor.model; public interface MonitorDataPublisher { void publish(MonitorDataProvider provider); default void init() { } } ================================================ FILE: huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.huaweicloud.dashboard.monitor.MonitorInformationCollector ================================================ FILE: huawei-cloud/dashboard/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.huaweicloud.dashboard.monitor.DashboardConfiguration ================================================ FILE: huawei-cloud/dashboard/src/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProviderTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.dashboard.monitor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class MetricsMonitorDataProviderTest { @Test public void testCodeMatch() { Assertions.assertTrue("200".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); Assertions.assertTrue("202".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); Assertions.assertFalse("2002".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); Assertions.assertFalse("400".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); } } ================================================ FILE: huawei-cloud/dashboard/src/test/resources/microservice.yaml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # spring boot configurations server: port: 9093 # should be same with servicecomb.rest.address to use web container # override common configurations in common module servicecomb-config-order: 10 instance_description: environment: production servicecomb: service: environment: development application: demo-java-chassis-dashboard name: test-dashboard version: 0.0.1 registry: address: http://127.0.0.1:30100 instance: watch: false kie: serverUri: http://127.0.0.1:30110 customLabel: public rest: address: 0.0.0.0:9097 # should be same with server.port to use web container ================================================ FILE: huawei-cloud/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default huawei-cloud Java Chassis::Huawei Cloud pom 4.0.0 servicestage dashboard darklaunch ================================================ FILE: huawei-cloud/servicestage/pom.xml ================================================ huawei-cloud org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 servicestage Java Chassis::Huawei Cloud::ServiceStage org.apache.servicecomb java-chassis-core org.apache.servicecomb registry-service-center org.apache.servicecomb foundation-test-scaffolding org.apache.logging.log4j log4j-slf4j-impl test ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.auth.Cipher; import org.apache.servicecomb.foundation.auth.DefaultCipher; import org.apache.servicecomb.foundation.auth.ShaAKSKCipher; import org.apache.servicecomb.foundation.bootstrap.BootStrapService; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; public class AKSKAuthHeaderProvider implements AuthHeaderProvider, BootStrapService { private static final Logger LOGGER = LoggerFactory.getLogger(AKSKAuthHeaderProvider.class); private static final String CONFIG_AKSK_ENABLED = "servicecomb.credentials.akskEnabled"; private static final String CONFIG_ACCESS_KEY = "servicecomb.credentials.accessKey"; private static final String CONFIG_SECRET_KEY = "servicecomb.credentials.secretKey"; private static final String CONFIG_CIPHER = "servicecomb.credentials.akskCustomCipher"; private static final String CONFIG_PROJECT = "servicecomb.credentials.project"; private static final String VALUE_DEFAULT_PROJECT = "default"; private static final String VALUE_DEFAULT_CIPHER = "default"; private static final String X_SERVICE_AK = "X-Service-AK"; private static final String X_SERVICE_SHAAKSK = "X-Service-ShaAKSK"; private static final String X_SERVICE_PROJECT = "X-Service-Project"; private static Environment environment; private final Map headers = new HashMap<>(); private boolean loaded = false; public AKSKAuthHeaderProvider() { } public Map authHeaders() { if (!environment.getProperty(CONFIG_AKSK_ENABLED, boolean.class, true)) { return Collections.emptyMap(); } if (StringUtils.isEmpty(getAccessKey())) { LOGGER.warn("ak sk auth enabled but access key is not configured, disable it at runtime. " + "Config [{}] to false to disable it implicitly.", CONFIG_AKSK_ENABLED); return Collections.emptyMap(); } if (!loaded) { load(); } return headers; } private synchronized void load() { if (!loaded) { headers.put(X_SERVICE_AK, getAccessKey()); headers.put(X_SERVICE_SHAAKSK, getSecretKey()); headers.put(X_SERVICE_PROJECT, getProject()); loaded = true; } } private String getAccessKey() { return environment.getProperty(CONFIG_ACCESS_KEY, ""); } private String getCipher() { return environment.getProperty(CONFIG_CIPHER, VALUE_DEFAULT_CIPHER); } private String getSecretKey() { String secretKey = environment.getProperty(CONFIG_SECRET_KEY, ""); String decodedSecretKey = new String(findCipher().decrypt(secretKey.toCharArray())); // ShaAKSKCipher 不解密, 认证的时候不处理;其他算法解密为 plain,需要 encode 为 ShaAKSKCipher 去认证。 if (ShaAKSKCipher.CIPHER_NAME.equalsIgnoreCase(getCipher())) { return decodedSecretKey; } else { return sha256Encode(decodedSecretKey, getAccessKey()); } } private String getProject() { String project = environment.getProperty(CONFIG_PROJECT, VALUE_DEFAULT_PROJECT); if (StringUtils.isEmpty(project)) { return project; } return URLEncoder.encode(project, StandardCharsets.UTF_8); } private Cipher findCipher() { if (DefaultCipher.CIPHER_NAME.equals(getCipher())) { return DefaultCipher.getInstance(); } List ciphers = SPIServiceUtils.getOrLoadSortedService(Cipher.class); return ciphers.stream().filter(c -> c.name().equals(getCipher())).findFirst() .orElseThrow(() -> new IllegalArgumentException("failed to find cipher named " + getCipher())); } public static String sha256Encode(String key, String data) { try { Mac sha256HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); sha256HMAC.init(secretKey); return Hex.encodeHexString(sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8))); } catch (Exception e) { throw new IllegalArgumentException("Can not encode ak sk. Please check the value is correct.", e); } } @Override public void startup(Environment environment) { AKSKAuthHeaderProvider.environment = environment; } } ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import static org.apache.commons.lang3.StringUtils.EMPTY; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; public class CasEnvConfig { private static final String APPLICATION_ID = "CAS_APPLICATION_ID"; private static final String COMPONENT_NAME = "CAS_COMPONENT_NAME"; private static final String INSTANCE_VERSION = "CAS_INSTANCE_VERSION"; private static final String INSTANCE_ID = "CAS_INSTANCE_ID"; private static final String ENVIRONMENT_ID = "CAS_ENVIRONMENT_ID"; private static final String SERVICE_PROPS = "SERVICECOMB_SERVICE_PROPS"; private static final String INSTANCE_PROPS = "SERVICECOMB_INSTANCE_PROPS"; public static final CasEnvConfig INSTANCE = new CasEnvConfig(); private Map parseProps(String value) { Map rs = new HashMap<>(); if (StringUtils.isEmpty(value)) { return rs; } return Arrays.stream(value.split(",")).map(v -> v.split(":")) .filter(v -> v.length == 2) .collect(Collectors.toMap(v -> v[0], v -> v[1])); } public Map getNonEmptyInstanceProperties() { Map map = new HashMap<>(); map.put(APPLICATION_ID, LegacyPropertyFactory .getStringProperty(APPLICATION_ID, EMPTY)); map.put(COMPONENT_NAME, LegacyPropertyFactory .getStringProperty(COMPONENT_NAME, EMPTY)); map.put(INSTANCE_VERSION, LegacyPropertyFactory .getStringProperty(INSTANCE_VERSION, EMPTY)); map.put(INSTANCE_ID, LegacyPropertyFactory .getStringProperty(INSTANCE_ID, EMPTY)); map.put(ENVIRONMENT_ID, LegacyPropertyFactory .getStringProperty(ENVIRONMENT_ID, EMPTY)); Map instanceProps = map.entrySet().stream() .filter(entry -> StringUtils.isNotEmpty(entry.getValue())) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); instanceProps.putAll(parseProps(LegacyPropertyFactory .getStringProperty(INSTANCE_PROPS, EMPTY))); return instanceProps; } public Map getNonEmptyServiceProperties() { return parseProps(LegacyPropertyFactory .getStringProperty(SERVICE_PROPS, EMPTY)); } } ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvVariablesAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import java.util.Map; import org.apache.servicecomb.registry.RegistrationManager; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; public class CasEnvVariablesAdapter implements InitializingBean { private RegistrationManager registrationManager; @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; } @Override public void afterPropertiesSet() throws Exception { Map extras = CasEnvConfig.INSTANCE.getNonEmptyServiceProperties(); extras.forEach((k, v) -> this.registrationManager.addProperty(k, v)); extras = CasEnvConfig.INSTANCE.getNonEmptyInstanceProperties(); extras.forEach((k, v) -> this.registrationManager.addProperty(k, v)); } } ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.foundation.auth.Cipher; import org.apache.servicecomb.foundation.auth.DefaultCipher; import org.apache.servicecomb.foundation.bootstrap.BootStrapService; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.vertx.VertxConst; import org.apache.servicecomb.http.client.auth.DefaultRequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.registry.sc.SCClientUtils; import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterRawClient; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; public class RBACBootStrapService implements BootStrapService { private static final String RBAC_ADDRESS = "servicecomb.service.registry.address"; public static final String DEFAULT_REGISTRY_NAME = "default"; public static final String RBAC_ENABLED = "servicecomb.credentials.rbac.enabled"; public static final String ACCOUNT_NAME_KEY = "servicecomb.credentials.account.name"; public static final String PASSWORD_KEY = "servicecomb.credentials.account.password"; public static final String CIPHER_KEY = "servicecomb.credentials.cipher"; public static final String PROJECT_KEY = "servicecomb.credentials.project"; private static final String SSL_TAG = "sc.consumer"; @Override public void startup(Environment environment) { if (!getBooleanProperty(environment, false, RBAC_ENABLED)) { return; } ServiceCenterAddressManager addressManager = createAddressManager(environment); addressManager.setEventBus(EventManager.getEventBus()); SSLProperties sslProperties = createSSLProperties(environment); sslProperties.setEnabled(addressManager.sslEnabled()); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(new ServiceCenterRawClient.Builder() .setTenantName("default") .setAddressManager(addressManager) .setHttpTransport(createHttpTransport(environment, sslProperties)).build(), addressManager); Map clients = new HashMap<>(1); clients.put(DEFAULT_REGISTRY_NAME, serviceCenterClient); TokenCacheManager.getInstance().setServiceCenterClients(clients); TokenCacheManager.getInstance().addTokenCache( DEFAULT_REGISTRY_NAME, getStringProperty(environment, null, ACCOUNT_NAME_KEY), getStringProperty(environment, null, PASSWORD_KEY), getCipher(getStringProperty(environment, DefaultCipher.CIPHER_NAME, CIPHER_KEY))); } private static HttpTransport createHttpTransport(Environment environment, SSLProperties sslProperties) { if (isProxyEnable(environment)) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(HttpTransportFactory.defaultRequestConfig().build()); HttpHost proxy = new HttpHost(getProxyHost(environment), getProxyPort(environment), "http"); // now only support http proxy httpClientBuilder.setProxy(proxy); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(getProxyUsername(environment), getProxyPasswd(environment))); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return HttpTransportFactory .createHttpTransport(sslProperties, new DefaultRequestAuthHeaderProvider(), httpClientBuilder); } return HttpTransportFactory .createHttpTransport(sslProperties, new DefaultRequestAuthHeaderProvider(), HttpTransportFactory.defaultRequestConfig().build()); } @VisibleForTesting Cipher getCipher(String cipherName) { if (DefaultCipher.CIPHER_NAME.equals(cipherName)) { return DefaultCipher.getInstance(); } List ciphers = SPIServiceUtils.getOrLoadSortedService(Cipher.class); return ciphers.stream().filter(c -> c.name().equals(cipherName)).findFirst() .orElseThrow(() -> new IllegalArgumentException("failed to find cipher named " + cipherName)); } private ServiceCenterAddressManager createAddressManager(Environment environment) { return SCClientUtils.createAddressManager(getProjectName(environment), getRBACAddressList(environment), environment); } private SSLProperties createSSLProperties(Environment environment) { SSLProperties sslProperties = new SSLProperties(); SSLOption option = new SSLOption(); option.setEngine(getStringProperty(environment, DEFAULT_OPTION.getEngine(), "ssl." + SSL_TAG + ".engine", "ssl.engine")); option.setProtocols( getStringProperty(environment, DEFAULT_OPTION.getProtocols(), "ssl." + SSL_TAG + ".protocols", "ssl.protocols")); option.setCiphers( getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + SSL_TAG + ".ciphers", "ssl.ciphers")); option.setAuthPeer( getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + SSL_TAG + ".authPeer", "ssl.authPeer")); option.setCheckCNHost( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNHost(), "ssl." + SSL_TAG + ".checkCN.host", "ssl.checkCN.host")); option.setCheckCNWhite( getBooleanProperty(environment, DEFAULT_OPTION.isCheckCNWhite(), "ssl." + SSL_TAG + ".checkCN.white", "ssl.checkCN.white")); option.setCheckCNWhiteFile(getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + SSL_TAG + ".checkCN.white.file", "ssl.checkCN.white.file")); option.setAllowRenegotiate(getBooleanProperty(environment, DEFAULT_OPTION.isAllowRenegotiate(), "ssl." + SSL_TAG + ".allowRenegotiate", "ssl.allowRenegotiate")); option.setStorePath( getStringProperty(environment, DEFAULT_OPTION.getStorePath(), "ssl." + SSL_TAG + ".storePath", "ssl.storePath")); option.setClientAuth( getStringProperty(environment, DEFAULT_OPTION.getClientAuth(), "ssl." + SSL_TAG + ".clientAuth", "ssl.clientAuth")); option.setTrustStore( getStringProperty(environment, DEFAULT_OPTION.getTrustStore(), "ssl." + SSL_TAG + ".trustStore", "ssl.trustStore")); option.setTrustStoreType(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreType(), "ssl." + SSL_TAG + ".trustStoreType", "ssl.trustStoreType")); option.setTrustStoreValue(getStringProperty(environment, DEFAULT_OPTION.getTrustStoreValue(), "ssl." + SSL_TAG + ".trustStoreValue", "ssl.trustStoreValue")); option.setKeyStore( getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + SSL_TAG + ".keyStore", "ssl.keyStore")); option.setKeyStoreType( getStringProperty(environment, DEFAULT_OPTION.getKeyStoreType(), "ssl." + SSL_TAG + ".keyStoreType", "ssl.keyStoreType")); option.setKeyStoreValue(getStringProperty(environment, DEFAULT_OPTION.getKeyStoreValue(), "ssl." + SSL_TAG + ".keyStoreValue", "ssl.keyStoreValue")); option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + SSL_TAG + ".crl", "ssl.crl")); option.setSslCustomClass( getStringProperty(environment, null, "ssl." + SSL_TAG + ".sslCustomClass", "ssl.sslCustomClass")); sslProperties.setSslOption(option); sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); return sslProperties; } private String getStringProperty(Environment environment, String defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return environment.getProperty(key); } } return defaultValue; } private boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { for (String key : keys) { if (environment.getProperty(key) != null) { return Boolean.parseBoolean(environment.getProperty(key)); } } return defaultValue; } private String getProjectName(Environment environment) { return getStringProperty(environment, "default", PROJECT_KEY); } private List getRBACAddressList(Environment environment) { String address = environment.getProperty(RBAC_ADDRESS, "http://127.0.0.1:30100"); return Arrays.asList(address.split(",")); } public static Boolean isProxyEnable(Environment environment) { return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); } public static String getProxyHost(Environment environment) { return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); } public static int getProxyPort(Environment environment) { return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); } public static String getProxyUsername(Environment environment) { return environment.getProperty(VertxConst.PROXY_USERNAME); } public static String getProxyPasswd(Environment environment) { return environment.getProperty(VertxConst.PROXY_PASSWD); } } ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ServiceStageConfiguration { @Bean public CasEnvVariablesAdapter casEnvVariablesAdapter() { return new CasEnvVariablesAdapter(); } } ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; public class TokenAuthHeaderProvider implements AuthHeaderProvider { @Override public Map authHeaders(String host) { String token = TokenCacheManager.getInstance().getToken(host); if (StringUtils.isEmpty(token)) { return new HashMap<>(); } HashMap header = new HashMap<>(); header.put("Authorization", "Bearer " + token); return Collections.unmodifiableMap(header); } } ================================================ FILE: huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import java.net.URI; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.auth.Cipher; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.http.client.event.OperationEvents; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.eventbus.Subscribe; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import jakarta.ws.rs.core.Response.Status; public final class TokenCacheManager { private static final Logger LOGGER = LoggerFactory.getLogger(TokenCacheManager.class); // special token used for special conditions // e.g. un-authorized: will query token after token expired period // e.g. not found: will query token after token expired period public static final String INVALID_TOKEN = ""; private static final TokenCacheManager INSTANCE = new TokenCacheManager(); private Map serviceCenterClients; private TokenCache tokenCache; public static TokenCacheManager getInstance() { return INSTANCE; } private TokenCacheManager() { } public void setServiceCenterClients(Map serviceCenterClients) { this.serviceCenterClients = serviceCenterClients; } public void addTokenCache(String registryName, String accountName, String password, Cipher cipher) { tokenCache = new TokenCache(registryName, accountName, password, cipher); } public String getToken(String host) { if (tokenCache == null) { return null; } return tokenCache.getToken(host); } public class TokenCache { private static final long TOKEN_REFRESH_TIME_IN_SECONDS = 20 * 60 * 1000; private final String registryName; private final String accountName; private final String password; private ExecutorService executorService; private LoadingCache cache; private final Cipher cipher; public TokenCache(String registryName, String accountName, String password, Cipher cipher) { this.registryName = registryName; this.accountName = accountName; this.password = password; this.cipher = cipher; if (enabled()) { executorService = Executors.newFixedThreadPool(1, t -> new Thread(t, "rbac-executor-" + this.registryName) { @Override public void run() { try { super.run(); } catch (Throwable e) { LOGGER.error("", e); } } }); cache = CacheBuilder.newBuilder() .maximumSize(10) .refreshAfterWrite(refreshTime(), TimeUnit.MILLISECONDS) .build(new CacheLoader() { @Override public String load(String key) throws Exception { return createHeaders(key); } @Override public ListenableFuture reload(String key, String oldValue) throws Exception { return Futures.submit(() -> createHeaders(key), executorService); } }); EventManager.getEventBus().register(this); } } @Subscribe public void onNotPermittedEvent(OperationEvents.UnAuthorizedOperationEvent event) { LOGGER.warn("address {} unAuthorized, refresh cache token!", event.getAddress()); cache.refresh(getHostByAddress(event.getAddress())); } private String getHostByAddress(String address) { try { URI uri = URI.create(address); return uri.getHost(); } catch (Exception e) { LOGGER.error("get host by address [{}] error!", address, e); return registryName; } } private String createHeaders(String host) { LOGGER.info("start to create RBAC headers for host: {}", host); ServiceCenterClient serviceCenterClient = serviceCenterClients.get(this.registryName); RbacTokenRequest request = new RbacTokenRequest(); request.setName(accountName); request.setPassword(new String(cipher.decrypt(password.toCharArray()))); RbacTokenResponse rbacTokenResponse = serviceCenterClient.queryToken(request, host); if (Status.UNAUTHORIZED.getStatusCode() == rbacTokenResponse.getStatusCode() || Status.FORBIDDEN.getStatusCode() == rbacTokenResponse.getStatusCode()) { // password wrong, do not try anymore LOGGER.warn("username or password may be wrong, stop trying to query tokens."); return INVALID_TOKEN; } if (Status.NOT_FOUND.getStatusCode() == rbacTokenResponse.getStatusCode()) { // service center not support, do not try LOGGER.warn("service center do not support RBAC token, you should not config account info"); return INVALID_TOKEN; } if (Status.INTERNAL_SERVER_ERROR.getStatusCode() == rbacTokenResponse.getStatusCode()) { // return null for server_error, so the token information can be re-fetched on the next call. // It will prompt 'CacheLoader returned null for key xxx' LOGGER.warn("service center query RBAC token error!"); return null; } LOGGER.info("refresh host [{}] token successfully {}", host, rbacTokenResponse.getStatusCode()); return rbacTokenResponse.getToken(); } protected long refreshTime() { return TOKEN_REFRESH_TIME_IN_SECONDS; } public String getToken(String host) { if (!enabled()) { return null; } String address = host; if (StringUtils.isEmpty(address)) { address = registryName; } try { return cache.get(address); } catch (Exception e) { LOGGER.error("failed to create token", e); return null; } } private boolean enabled() { return !StringUtils.isEmpty(this.accountName) && !StringUtils.isEmpty(this.password); } } } ================================================ FILE: huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.huaweicloud.servicestage.AKSKAuthHeaderProvider org.apache.servicecomb.huaweicloud.servicestage.TokenAuthHeaderProvider ================================================ FILE: huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.foundation.auth.ShaAKSKCipher ================================================ FILE: huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.huaweicloud.servicestage.RBACBootStrapService org.apache.servicecomb.huaweicloud.servicestage.AKSKAuthHeaderProvider ================================================ FILE: huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.huaweicloud.servicestage.CasEnvVariablesAdapter ================================================ FILE: huawei-cloud/servicestage/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.huaweicloud.servicestage.ServiceStageConfiguration ================================================ FILE: huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestAKSKAuthHeaderProvider { @Test public void test_project_name_properly_encoded_en() { Environment environment = Mockito.mock(Environment.class); Mockito.when(environment.getProperty("servicecomb.credentials.akskEnabled", boolean.class, true)).thenReturn(true); Mockito.when(environment.getProperty("servicecomb.credentials.project", "default")).thenReturn("hello"); Mockito.when(environment.getProperty("servicecomb.credentials.accessKey", "")).thenReturn("access key"); Mockito.when(environment.getProperty("servicecomb.credentials.secretKey", "")).thenReturn("secret key"); Mockito.when(environment.getProperty("servicecomb.credentials.akskCustomCipher", "default")).thenReturn("default"); AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(); provider.startup(environment); Assertions.assertEquals("hello", provider.authHeaders().get("X-Service-Project")); } @Test public void test_project_name_properly_encoded_cn() { Environment environment = Mockito.mock(Environment.class); Mockito.when(environment.getProperty("servicecomb.credentials.akskEnabled", boolean.class, true)).thenReturn(true); Mockito.when(environment.getProperty("servicecomb.credentials.project", "default")).thenReturn("测试"); Mockito.when(environment.getProperty("servicecomb.credentials.accessKey", "")).thenReturn("access key"); Mockito.when(environment.getProperty("servicecomb.credentials.secretKey", "")).thenReturn("secret key"); Mockito.when(environment.getProperty("servicecomb.credentials.akskCustomCipher", "default")).thenReturn("default"); AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(); provider.startup(environment); Assertions.assertEquals("%E6%B5%8B%E8%AF%95", provider.authHeaders().get("X-Service-Project")); } } ================================================ FILE: huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.huaweicloud.servicestage; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class TestEnvVariablesAdapter { @BeforeAll public static void init() { System.setProperty("CAS_APPLICATION_ID", "application-id"); System.setProperty("CAS_ENVIRONMENT_ID", "env-id"); System.setProperty("SERVICECOMB_SERVICE_PROPS", "component:ConsumerService,other:A"); System.setProperty("SERVICECOMB_INSTANCE_PROPS", "route:gray"); } @Test public void testProcessInstance() { // TODO: set CAS properties for service center CasEnvVariablesAdapter adapter = new CasEnvVariablesAdapter(); // MicroserviceInstance instance = new MicroserviceInstance(); // adapter.beforeRegisterInstance(instance); // // Assertions.assertEquals(3, instance.getProperties().size()); // Assertions.assertEquals("application-id", instance.getProperties().get("CAS_APPLICATION_ID")); // Assertions.assertEquals("env-id", instance.getProperties().get("CAS_ENVIRONMENT_ID")); // Assertions.assertEquals("gray", instance.getProperties().get("route")); // // Microservice microservice = new Microservice(); // adapter.beforeRegisterService(microservice); // Assertions.assertEquals(2, microservice.getProperties().size()); // Assertions.assertEquals("ConsumerService", microservice.getProperties().get("component")); // Assertions.assertEquals("A", microservice.getProperties().get("other")); } @AfterAll public static void destroy() { System.getProperties().remove("CAS_APPLICATION_ID"); System.getProperties().remove("CAS_ENVIRONMENT_ID"); System.getProperties().remove("SERVICECOMB_SERVICE_PROPS"); System.getProperties().remove("SERVICECOMB_INSTANCE_PROPS"); } } ================================================ FILE: huawei-cloud/servicestage/src/test/resources/log4j2.xml ================================================ ================================================ FILE: metrics/metrics-core/pom.xml ================================================ metrics org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 metrics-core Java Chassis::Metrics::Core org.apache.servicecomb foundation-metrics org.apache.servicecomb java-chassis-core org.apache.servicecomb swagger-generator-jaxrs org.apache.servicecomb common-rest org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb registry-service-center test io.vertx vertx-codegen provided org.mockito mockito-inline test org.jmockit jmockit test ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.metrics.core.meter.ConsumerMeters; import org.apache.servicecomb.metrics.core.meter.EdgeMeters; import org.apache.servicecomb.metrics.core.meter.ProducerMeters; import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import io.micrometer.core.instrument.MeterRegistry; public class InvocationMetersInitializer implements MetricsInitializer { private ConsumerMeters consumerMeters; private ProducerMeters producerMeters; private EdgeMeters edgeMeters; @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { consumerMeters = new ConsumerMeters(meterRegistry, config); producerMeters = new ProducerMeters(meterRegistry, config); edgeMeters = new EdgeMeters(meterRegistry, config); eventBus.register(this); } protected AbstractInvocationMeters findInvocationMeters(Invocation invocation) { if (invocation.isConsumer()) { return consumerMeters.getInvocationMeters(); } if (invocation.isEdge()) { return edgeMeters.getInvocationMeters(); } return producerMeters.getInvocationMeters(); } @Subscribe @AllowConcurrentEvents public void onInvocationStart(InvocationStartEvent event) { AbstractInvocationMeters invocationMeters = findInvocationMeters(event.getInvocation()); invocationMeters.onInvocationStart(event); } @Subscribe @AllowConcurrentEvents public void onInvocationFinish(InvocationFinishEvent event) { AbstractInvocationMeters invocationMeters = findInvocationMeters(event.getInvocation()); invocationMeters.onInvocationFinish(event); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.metrics.MetricsBootstrap; public class MetricsBootListener implements BootListener { private final MetricsBootstrap metricsBootstrap; public MetricsBootListener(MetricsBootstrap metricsBootstrap) { this.metricsBootstrap = metricsBootstrap; } @Override public void onBeforeProducerProvider(BootEvent event) { } public MetricsBootstrap getMetricsBootstrap() { return metricsBootstrap; } @Override public void onAfterRegistry(BootEvent event) { metricsBootstrap.start(EventManager.getEventBus()); } @Override public void onBeforeClose(BootEvent event) { metricsBootstrap.shutdown(); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import java.time.Duration; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.foundation.metrics.MetricsBootstrap; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher; import org.apache.servicecomb.metrics.core.publish.SlowInvocationLogger; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.CountingMode; @Configuration public class MetricsCoreConfiguration { @Bean public MetricsBootstrapConfig metricsBootstrapConfig(Environment environment) { return new MetricsBootstrapConfig(environment); } @Bean @ConditionalOnMissingBean public MeterRegistry meterRegistry(MetricsBootstrapConfig config) { return new SimpleMeterRegistryExt(s -> { if ("simple.step".equals(s)) { return Duration.ofMillis(config.getMsPollInterval()).toString(); } if ("simple.mode".equals(s)) { return CountingMode.STEP.name(); } return null; }, Clock.SYSTEM); } @Bean public MetricsBootListener metricsBootListener(MetricsBootstrap metricsBootstrap) { return new MetricsBootListener(metricsBootstrap); } @Bean public MetricsBootstrap metricsBootstrap(MetricsBootstrapConfig config) { return new MetricsBootstrap(config); } // Begin MetricsInitializers @Bean public DefaultLogPublisher defaultLogPublisher() { return new DefaultLogPublisher(); } @Bean public InvocationMetersInitializer invocationMetersInitializer() { return new InvocationMetersInitializer(); } @Bean public ThreadPoolMetersInitializer threadPoolMetersInitializer() { return new ThreadPoolMetersInitializer(); } @Bean public VertxMetersInitializer vertxMetersInitializer() { return new VertxMetersInitializer(); } @Bean public OsMetersInitializer osMetersInitializer() { return new OsMetersInitializer(); } @Bean public SlowInvocationLogger slowInvocationLogger(SCBEngine scbEngine) { return new SlowInvocationLogger(scbEngine); } // End MetricsInitializers } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; public class OsMetersInitializer implements MetricsInitializer, PeriodMeter { private OsMeter osMeter; @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { osMeter = new OsMeter(meterRegistry); } public OsMeter getOsMeter() { return osMeter; } @Override public void poll(long msNow, long secondInterval) { if (osMeter != null) { osMeter.poll(msNow, secondInterval); } } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.cumulative.CumulativeDistributionSummary; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.core.instrument.distribution.HistogramGauges; import io.micrometer.core.instrument.distribution.StepBucketHistogram; import io.micrometer.core.instrument.simple.SimpleConfig; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; /** * Custom SimpleMeterRegistry to support StepDistributionSummary */ public class SimpleMeterRegistryExt extends SimpleMeterRegistry { private final SimpleConfig config; public SimpleMeterRegistryExt(SimpleConfig config, Clock clock) { super(config, clock); this.config = config; } @Override protected DistributionSummary newDistributionSummary(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) { DistributionStatisticConfig merged = distributionStatisticConfig .merge(DistributionStatisticConfig.builder().expiry(config.step()).build()); DistributionSummary summary; switch (config.mode()) { case CUMULATIVE: summary = new CumulativeDistributionSummary(id, clock, merged, scale, false); break; case STEP: default: summary = new StepDistributionSummaryExt(id, clock, merged, scale, config.step().toMillis(), new StepBucketHistogram(clock, config.step().toMillis(), distributionStatisticConfig, false, false)); break; } HistogramGauges.registerWithCommonFormat(summary, this); return summary; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.core.instrument.distribution.Histogram; import io.micrometer.core.instrument.step.StepDistributionSummary; public class StepDistributionSummaryExt extends StepDistributionSummary { public StepDistributionSummaryExt(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig, double scale, long stepMillis, Histogram histogram) { super(id, clock, distributionStatisticConfig, scale, stepMillis, histogram); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SystemMetrics.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; import java.lang.management.ThreadMXBean; public class SystemMetrics { private final OperatingSystemMXBean systemMXBean; private final ThreadMXBean threadMXBean; private final MemoryMXBean memoryMXBean; public SystemMetrics() { this(ManagementFactory.getOperatingSystemMXBean(), ManagementFactory.getThreadMXBean(), ManagementFactory.getMemoryMXBean()); } public SystemMetrics(OperatingSystemMXBean systemMXBean, ThreadMXBean threadMXBean, MemoryMXBean memoryMXBean) { this.systemMXBean = systemMXBean; this.threadMXBean = threadMXBean; this.memoryMXBean = memoryMXBean; } public double getCpuLoad() { return systemMXBean.getSystemLoadAverage(); } public int getCpuRunningThreads() { return threadMXBean.getThreadCount(); } public long getHeapInit() { return memoryMXBean.getHeapMemoryUsage().getInit(); } public long getHeapMax() { return memoryMXBean.getHeapMemoryUsage().getMax(); } public long getHeapCommit() { return memoryMXBean.getHeapMemoryUsage().getCommitted(); } public long getHeapUsed() { return memoryMXBean.getHeapMemoryUsage().getUsed(); } public long getNonHeapInit() { return memoryMXBean.getNonHeapMemoryUsage().getInit(); } public long getNonHeapMax() { return memoryMXBean.getNonHeapMemoryUsage().getMax(); } public long getNonHeapCommit() { return memoryMXBean.getNonHeapMemoryUsage().getCommitted(); } public long getNonHeapUsed() { return memoryMXBean.getNonHeapMemoryUsage().getUsed(); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.executor.GroupExecutor; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; public class ThreadPoolMetersInitializer implements MetricsInitializer, PeriodMeter { private MeterRegistry meterRegistry; private Map threadPoolMeters = new HashMap<>(); @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { this.meterRegistry = meterRegistry; createThreadPoolMeters(); } public void createThreadPoolMeters() { Map operationExecutors = collectionOperationExecutors(); // currently, all operation executors come from bean Map beanExecutors = BeanUtils.getContext().getBeansOfType(Executor.class); for (Entry entry : beanExecutors.entrySet()) { Executor executor = entry.getValue(); if (!operationExecutors.containsKey(executor)) { continue; } if (executor instanceof GroupExecutor) { createThreadPoolMeters(entry.getKey(), (GroupExecutor) executor); continue; } createThreadPoolMeters(entry.getKey(), executor); } } protected Map collectionOperationExecutors() { Map operationExecutors = new IdentityHashMap<>(); //only one instance in the values MicroserviceMeta microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); for (OperationMeta operationMeta : microserviceMeta.getOperations()) { operationExecutors.put(operationMeta.getExecutor(), operationMeta.getExecutor()); } return operationExecutors; } protected void createThreadPoolMeters(String threadPoolName, GroupExecutor groupExecutor) { for (int idx = 0; idx < groupExecutor.getExecutorList().size(); idx++) { Executor executor = groupExecutor.getExecutorList().get(idx); createThreadPoolMeters(threadPoolName + "-group" + idx, executor); } } public void createThreadPoolMeters(String threadPoolName, Executor executor) { if (!(executor instanceof ThreadPoolExecutor threadPoolExecutor)) { return; } threadPoolMeters.computeIfAbsent(threadPoolName, (key) -> new ThreadPoolMeter(meterRegistry, threadPoolName, threadPoolExecutor)); } @Override public void poll(long msNow, long secondInterval) { threadPoolMeters.forEach((key, value) -> value.poll(msNow, secondInterval)); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointsMeter; import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointsMeter; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class VertxMetersInitializer implements MetricsInitializer, PeriodMeter { public static final String VERTX_ENDPOINTS = "servicecomb.vertx.endpoints"; public static final String ENDPOINTS_TYPE = "type"; public static final String ENDPOINTS_CLIENT = "client"; public static final String ENDPOINTS_SERVER = "server"; private HttpClientEndpointsMeter httpClientEndpointsMeter; private ServerEndpointsMeter serverEndpointsMeter; @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { httpClientEndpointsMeter = new HttpClientEndpointsMeter(meterRegistry, VERTX_ENDPOINTS, Tags.of(ENDPOINTS_TYPE, ENDPOINTS_CLIENT), SharedVertxFactory.getMetricsFactory(config.getEnvironment()) .getVertxMetrics() .getClientEndpointMetricManager()); serverEndpointsMeter = new ServerEndpointsMeter(meterRegistry, VERTX_ENDPOINTS, Tags.of(ENDPOINTS_TYPE, ENDPOINTS_SERVER), SharedVertxFactory.getMetricsFactory(config.getEnvironment()) .getVertxMetrics() .getServerEndpointMetricMap()); } @Override public void poll(long msNow, long secondInterval) { httpClientEndpointsMeter.poll(msNow, secondInterval); serverEndpointsMeter.poll(msNow, secondInterval); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import org.apache.servicecomb.metrics.core.meter.invocation.ConsumerInvocationMeters; import io.micrometer.core.instrument.MeterRegistry; public class ConsumerMeters { private final AbstractInvocationMeters invocationMeters; public ConsumerMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { invocationMeters = new ConsumerInvocationMeters(meterRegistry, metricsBootstrapConfig); } public AbstractInvocationMeters getInvocationMeters() { return invocationMeters; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import org.apache.servicecomb.metrics.core.meter.invocation.EdgeInvocationMeters; import io.micrometer.core.instrument.MeterRegistry; public class EdgeMeters { private final AbstractInvocationMeters invocationMeters; public EdgeMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { this.invocationMeters = new EdgeInvocationMeters(meterRegistry, metricsBootstrapConfig); } public AbstractInvocationMeters getInvocationMeters() { return invocationMeters; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import org.apache.servicecomb.metrics.core.meter.invocation.ProducerInvocationMeters; import io.micrometer.core.instrument.MeterRegistry; public class ProducerMeters { private final AbstractInvocationMeters invocationMeters; public ProducerMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { invocationMeters = new ProducerInvocationMeters(meterRegistry, metricsBootstrapConfig); } public AbstractInvocationMeters getInvocationMeters() { return invocationMeters; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; import io.micrometer.core.instrument.Measurement; public class ThreadPoolMonitorPublishModelFactory { interface Setter { void set(ThreadPoolPublishModel model, Measurement measurement); } public static Map create(MeasurementTree tree) { Map result = new HashMap<>(8); readMeasurement(result, tree, ThreadPoolMeter.TASK_COUNT, (model, measurement) -> model.setAvgTaskCount(measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.COMPLETED_TASK_COUNT, (model, measurement) -> model.setAvgCompletedTaskCount(measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.CURRENT_THREADS_BUSY, (model, measurement) -> model.setCurrentThreadsBusy((int) measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.MAX_THREADS, (model, measurement) -> model.setMaxThreads((int) measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.POOL_SIZE, (model, measurement) -> model.setPoolSize((int) measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.CORE_POOL_SIZE, (model, measurement) -> model.setCorePoolSize((int) measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.QUEUE_SIZE, (model, measurement) -> model.setQueueSize((int) measurement.getValue())); readMeasurement(result, tree, ThreadPoolMeter.REJECTED_COUNT, (model, measurement) -> model.setRejected(measurement.getValue())); return result; } protected static void readMeasurement(Map threadPools, MeasurementTree tree, String name, Setter setter) { MeasurementNode node = tree.findChild(ThreadPoolMeter.THREAD_POOL_METER); if (node == null) { return; } for (String threadPoolName : node.getChildren().keySet()) { MeasurementNode measure = node.findChild(threadPoolName, name); if (measure == null) { continue; } ThreadPoolPublishModel model = threadPools.computeIfAbsent(threadPoolName, tpn -> new ThreadPoolPublishModel()); setter.set(model, measure.getMeasurements().get(0)); } } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import java.time.Duration; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; public abstract class AbstractInvocationMeter { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractInvocationMeter.class); // total time distribution private final DistributionSummary totalSummary; //total time private final Timer totalTimer; // prepare time private final Timer prepareTimer; protected final MetricsBootstrapConfig metricsBootstrapConfig; public AbstractInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, MetricsBootstrapConfig metricsBootstrapConfig) { this.metricsBootstrapConfig = metricsBootstrapConfig; double[] sla = toSla(metricsBootstrapConfig.getLatencyDistribution()); if (sla != null) { totalSummary = DistributionSummary.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_DISTRIBUTION)) .distributionStatisticExpiry(Duration.ofMillis(metricsBootstrapConfig.getMsPollInterval())) .serviceLevelObjectives(sla).register(meterRegistry); } else { totalSummary = null; } this.totalTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE , MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_TOTAL)).register(meterRegistry); this.prepareTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE , MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PREPARE)).register(meterRegistry); } private static double[] toSla(String config) { if (StringUtils.isEmpty(config)) { return null; } config = config.trim() + "," + LatencyDistributionConfig.MAX_LATENCY; String[] array = config.split("\\s*,+\\s*"); double[] result = new double[array.length]; for (int idx = 0; idx < array.length - 1; idx++) { long msMin = Long.parseLong(array[idx]); long msMax = Long.parseLong(array[idx + 1]); if (msMin >= msMax) { LOGGER.error("invalid latency scope, min={}, max={}.", array[idx], array[idx + 1]); return null; } result[idx] = msMin; } result[array.length - 1] = LatencyDistributionConfig.MAX_LATENCY; if (Double.compare(0, result[0]) == 0) { double[] target = new double[result.length - 1]; System.arraycopy(result, 1, target, 0, target.length); return target; } return result; } public void onInvocationFinish(InvocationFinishEvent event) { InvocationStageTrace stageTrace = event.getInvocation().getInvocationStageTrace(); totalTimer.record(stageTrace.calcTotal(), TimeUnit.NANOSECONDS); prepareTimer.record(stageTrace.calcPrepare(), TimeUnit.NANOSECONDS); if (totalSummary != null) { totalSummary.record(TimeUnit.NANOSECONDS.toMillis(stageTrace.calcTotal())); } } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import java.util.Map; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.swagger.invocation.Response; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public abstract class AbstractInvocationMeters { protected MeterRegistry registry; private final Map metersMap = new ConcurrentHashMapEx<>(); // not care for concurrency, just for make build key faster private int maxKeyLen = 64; protected MetricsBootstrapConfig metricsBootstrapConfig; public AbstractInvocationMeters(MeterRegistry registry, MetricsBootstrapConfig metricsBootstrapConfig) { this.registry = registry; this.metricsBootstrapConfig = metricsBootstrapConfig; } protected AbstractInvocationMeter getOrCreateMeters(Invocation invocation, Response response) { // build string key is faster than use Id to locate timer directly StringBuilder keyBuilder = new StringBuilder(maxKeyLen); String invocationName = invocation.getInvocationType().name(); keyBuilder .append(invocationName) .append(invocation.getRealTransportName()) .append(invocation.getMicroserviceQualifiedName()) .append(response.getStatusCode()); if (keyBuilder.length() > maxKeyLen) { maxKeyLen = keyBuilder.length(); } return metersMap.computeIfAbsent(keyBuilder.toString(), k -> { AbstractInvocationMeter meter = createMeter(MeterInvocationConst.INVOCATION_NAME, Tags.empty() .and(MeterInvocationConst.TAG_ROLE, invocationName) .and(MeterInvocationConst.TAG_TRANSPORT, invocation.getRealTransportName()) .and(MeterInvocationConst.TAG_OPERATION, invocation.getMicroserviceQualifiedName()) .and(MeterInvocationConst.TAG_STATUS, String.valueOf(response.getStatusCode()))); return meter; }); } protected abstract AbstractInvocationMeter createMeter(String name, Tags tags); public void onInvocationStart(InvocationStartEvent event) { } public void onInvocationFinish(InvocationFinishEvent event) { AbstractInvocationMeter meters = getOrCreateMeters(event.getInvocation(), event.getResponse()); meters.onInvocationFinish(event); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; public class ConsumerInvocationMeter extends AbstractInvocationMeter { private final Timer consumerEncodeRequestTimer; private final Timer consumerDecodeResponseTimer; private final Timer consumerGetConnectionTimer; private final Timer consumerSendRequestTimer; private final Timer consumerWaitResponseTimer; public ConsumerInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, MetricsBootstrapConfig metricsBootstrapConfig) { super(meterRegistry, name, tags, metricsBootstrapConfig); consumerSendRequestTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_SEND)).register(meterRegistry); consumerGetConnectionTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_CONNECTION)).register(meterRegistry); consumerEncodeRequestTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST)) .register(meterRegistry); consumerDecodeResponseTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE)) .register(meterRegistry); consumerWaitResponseTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_WAIT)).register(meterRegistry); } @Override public void onInvocationFinish(InvocationFinishEvent event) { super.onInvocationFinish(event); InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); consumerEncodeRequestTimer.record(invocationStageTrace.calcConsumerEncodeRequest(), TimeUnit.NANOSECONDS); consumerSendRequestTimer.record(invocationStageTrace.calcConsumerSendRequest(), TimeUnit.NANOSECONDS); consumerGetConnectionTimer.record(invocationStageTrace.calcConnection(), TimeUnit.NANOSECONDS); consumerWaitResponseTimer.record(invocationStageTrace.calcWait(), TimeUnit.NANOSECONDS); consumerDecodeResponseTimer.record(invocationStageTrace.calcConsumerDecodeResponse(), TimeUnit.NANOSECONDS); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class ConsumerInvocationMeters extends AbstractInvocationMeters { public ConsumerInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { super(meterRegistry, metricsBootstrapConfig); } @Override protected AbstractInvocationMeter createMeter(String name, Tags tags) { return new ConsumerInvocationMeter(registry, name, tags, this.metricsBootstrapConfig); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; public class EdgeInvocationMeter extends ConsumerInvocationMeter { private final Timer providerDecodeRequestTimer; private final Timer providerEncodeResponseTimer; private final Timer sendResponseTimer; public EdgeInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, MetricsBootstrapConfig metricsBootstrapConfig) { super(meterRegistry, name, tags, metricsBootstrapConfig); providerDecodeRequestTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST)) .register(meterRegistry); providerEncodeResponseTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE)) .register(meterRegistry); sendResponseTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_SEND)) .register(meterRegistry); } @Override public void onInvocationFinish(InvocationFinishEvent event) { super.onInvocationFinish(event); InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); providerDecodeRequestTimer.record(invocationStageTrace.calcProviderDecodeRequest(), TimeUnit.NANOSECONDS); providerEncodeResponseTimer.record(invocationStageTrace.calcProviderEncodeResponse(), TimeUnit.NANOSECONDS); sendResponseTimer.record(invocationStageTrace.calcProviderSendResponse(), TimeUnit.NANOSECONDS); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class EdgeInvocationMeters extends ConsumerInvocationMeters { public EdgeInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { super(meterRegistry, metricsBootstrapConfig); } @Override protected AbstractInvocationMeter createMeter(String name, Tags tags) { return new EdgeInvocationMeter(registry, name, tags, metricsBootstrapConfig); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; public interface MeterInvocationConst { String INVOCATION_NAME = "servicecomb.invocation"; // consumer or producer String TAG_ROLE = "role"; String TAG_OPERATION = "operation"; String TAG_TRANSPORT = "transport"; String TAG_TYPE = "type"; String TAG_STAGE = "stage"; String TAG_DISTRIBUTION = "distribution"; String TAG_STATUS = "status"; String EDGE_INVOCATION_NAME = "EDGE"; } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; public class ProducerInvocationMeter extends AbstractInvocationMeter { private final Timer executorQueueTimer; private final Timer executionTimer; private final Timer providerDecodeRequestTimer; private final Timer providerEncodeResponseTimer; private final Timer sendResponseTimer; public ProducerInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, MetricsBootstrapConfig metricsBootstrapConfig) { super(meterRegistry, name, tags, metricsBootstrapConfig); executorQueueTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_QUEUE)) .register(meterRegistry); executionTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_BUSINESS)) .register(meterRegistry); providerDecodeRequestTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST)) .register(meterRegistry); providerEncodeResponseTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE)) .register(meterRegistry); sendResponseTimer = Timer.builder(name) .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_SEND)) .register(meterRegistry); } @Override public void onInvocationFinish(InvocationFinishEvent event) { super.onInvocationFinish(event); InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); executorQueueTimer.record(invocationStageTrace.calcQueue(), TimeUnit.NANOSECONDS); executionTimer.record(invocationStageTrace.calcBusinessExecute(), TimeUnit.NANOSECONDS); providerDecodeRequestTimer.record(invocationStageTrace.calcProviderDecodeRequest(), TimeUnit.NANOSECONDS); providerEncodeResponseTimer.record(invocationStageTrace.calcProviderEncodeResponse(), TimeUnit.NANOSECONDS); sendResponseTimer.record(invocationStageTrace.calcProviderSendResponse(), TimeUnit.NANOSECONDS); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.invocation; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class ProducerInvocationMeters extends AbstractInvocationMeters { public ProducerInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { super(meterRegistry, metricsBootstrapConfig); } @Override protected AbstractInvocationMeter createMeter(String name, Tags tags) { return new ProducerInvocationMeter(registry, name, tags, metricsBootstrapConfig); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; public class NetMeter implements PeriodMeter { private static final Logger LOGGER = LoggerFactory.getLogger(NetMeter.class); public static final String STATISTIC = "statistic"; public static final String INTERFACE = "interface"; public static final Tag TAG_RECEIVE = Tag.of(STATISTIC, "receive"); public static final Tag TAG_PACKETS_RECEIVE = Tag.of(STATISTIC, "receivePackets"); public static final Tag TAG_SEND = Tag.of(STATISTIC, "send"); public static final Tag TAG_PACKETS_SEND = Tag.of(STATISTIC, "sendPackets"); private final Map interfaceUsageMap = new ConcurrentHashMap<>(); private boolean isOsLinux = SystemUtils.IS_OS_LINUX; protected final MeterRegistry meterRegistry; protected final String name; protected final Tags tags; public NetMeter(MeterRegistry meterRegistry, String name, Tags tags) { this.meterRegistry = meterRegistry; this.name = name; this.tags = tags; poll(0, 0); } @VisibleForTesting public void setOsLinux(boolean osLinux) { isOsLinux = osLinux; } @Override public void poll(long msNow, long secondInterval) { if (isOsLinux) { refreshNet(secondInterval); } } /* * Inter-| Receive | Transmit * face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed * eth0: 2615248100 32148518 0 0 0 0 0 0 87333034794 21420267 0 0 0 0 0 0 * 0 1 2 3 4 5 6 7 8 9 */ protected void refreshNet(long secondInterval) { try { File file = new File("/proc/net/dev"); List netInfo = FileUtils.readLines(file, StandardCharsets.UTF_8); //the first two lines is useless for (int i = 2; i < netInfo.size(); i++) { String interfaceData = netInfo.get(i); String[] strings = interfaceData.split(":"); if (strings.length != 2) { LOGGER.warn(" there is something wrong with {} ", interfaceData); continue; } String interfaceName = strings[0].trim(); InterfaceUsage interfaceUsage = interfaceUsageMap.computeIfAbsent(interfaceName, key -> new InterfaceUsage(meterRegistry, name, tags, key)); interfaceUsage.update(strings[1], secondInterval); } } catch (IOException e) { LOGGER.error("Failed to read net info/", e); } } public Map getInterfaceUsageMap() { return interfaceUsageMap; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import com.google.common.annotations.VisibleForTesting; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; /** * name=os type=cpu value = 0 * name=os type=processCpu value = 0 * name=os type=net interface=eth0 statistic=send value=100 * name=os type=net interface=eth0 statistic=receive value=100 * name=os type=net interface=eth0 statistic=sendPackets value=100 * name=os type=net interface=eth0 statistic=receivePackets value=100 */ public class OsMeter implements PeriodMeter { public static final String OS_NAME = "os"; public static final String OS_TYPE = "type"; public static final String OS_TYPE_NET = "net"; private final SystemMeter systemMeter; private final NetMeter netMeter; public OsMeter(MeterRegistry meterRegistry) { systemMeter = new SystemMeter(meterRegistry, OS_NAME); netMeter = new NetMeter(meterRegistry, OS_NAME, Tags.of(OS_TYPE, OS_TYPE_NET)); } @Override public void poll(long msNow, long secondInterval) { systemMeter.poll(msNow, secondInterval); netMeter.poll(msNow, secondInterval); } @VisibleForTesting public SystemMeter getCpuMeter() { return systemMeter; } @VisibleForTesting public NetMeter getNetMeter() { return netMeter; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os; import java.lang.management.ManagementFactory; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import com.google.common.annotations.VisibleForTesting; import com.sun.management.OperatingSystemMXBean; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class SystemMeter implements PeriodMeter { public static final String SYSTEM_LOAD_AVERAGE = "sla"; public static final String CPU_USAGE = "cpu"; public static final String PROCESS_CPU_USAGE = "processCpu"; public static final String MEMORY_USAGE = "memory"; private OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); public SystemMeter(MeterRegistry meterRegistry, String name) { Gauge.builder(name, () -> getOsBean().getSystemLoadAverage()) .tags(Tags.of(OsMeter.OS_TYPE, SYSTEM_LOAD_AVERAGE)) .register(meterRegistry); Gauge.builder(name, () -> getOsBean().getCpuLoad()) .tags(Tags.of(OsMeter.OS_TYPE, CPU_USAGE)) .register(meterRegistry); Gauge.builder(name, () -> getOsBean().getProcessCpuLoad()) .tags(Tags.of(OsMeter.OS_TYPE, PROCESS_CPU_USAGE)) .register(meterRegistry); Gauge.builder(name, () -> (getOsBean().getTotalMemorySize() - getOsBean().getFreeMemorySize()) / (double) getOsBean().getTotalMemorySize()) .tags(Tags.of(OsMeter.OS_TYPE, MEMORY_USAGE)) .register(meterRegistry); } @VisibleForTesting public void setOsBean(OperatingSystemMXBean mock) { this.osBean = mock; } private OperatingSystemMXBean getOsBean() { return this.osBean; } @Override public void poll(long msNow, long secondInterval) { } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os.net; import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.INTERFACE; import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_PACKETS_RECEIVE; import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_PACKETS_SEND; import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_RECEIVE; import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_SEND; import com.google.common.annotations.VisibleForTesting; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; public class InterfaceUsage { private final String interfaceName; private final NetStat receive; private final NetStat send; private final NetStat packetsReceive; private final NetStat packetsSend; public InterfaceUsage(MeterRegistry meterRegistry, String name, Tags tags, String interfaceName) { this.interfaceName = interfaceName; tags = tags.and(Tag.of(INTERFACE, interfaceName)); // recv/Bps receive = new NetStat(0); Gauge.builder(name, receive::getRate).tags(tags.and(TAG_RECEIVE)).register(meterRegistry); // send/Bps send = new NetStat(8); Gauge.builder(name, send::getRate).tags(tags.and(TAG_SEND)).register(meterRegistry); // recv/pps packetsReceive = new NetStat(1); Gauge.builder(name, packetsReceive::getRate).tags(tags.and(TAG_PACKETS_RECEIVE)).register(meterRegistry); // send/pps packetsSend = new NetStat(9); Gauge.builder(name, packetsSend::getRate).tags(tags.and(TAG_PACKETS_SEND)).register(meterRegistry); } public void update(String interfaceData, long secondInterval) { String[] netInfo = interfaceData.trim().split("\\s+"); receive.update(netInfo, secondInterval); send.update(netInfo, secondInterval); packetsReceive.update(netInfo, secondInterval); packetsSend.update(netInfo, secondInterval); } @VisibleForTesting public NetStat getReceive() { return receive; } @VisibleForTesting public NetStat getSend() { return send; } @VisibleForTesting public NetStat getPacketsReceive() { return packetsReceive; } @VisibleForTesting public NetStat getPacketsSend() { return packetsSend; } public String getName() { return interfaceName; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os.net; public class NetStat { private final int index; // send/recv bytes/packets private long lastValue; // Bps/pps private double rate; public NetStat(int index) { this.index = index; } public void clearRate() { rate = 0; } public void update(String[] netInfo, long secondInterval) { long currentValue = Long.parseLong(netInfo[index]); rate = (double) (currentValue - lastValue) / secondInterval; lastValue = currentValue; } public long getLastValue() { return lastValue; } public double getRate() { return rate; } public int getIndex() { return index; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.pool; import java.util.concurrent.ThreadPoolExecutor; import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; public class ThreadPoolMeter implements PeriodMeter { public static final String THREAD_POOL_METER = "servicecomb.threadpool"; public static final String ID = "id"; public static final String STAGE = "stage"; public static final String REJECTED_COUNT = "rejectedCount"; public static final String TASK_COUNT = "taskCount"; public static final String COMPLETED_TASK_COUNT = "completedTaskCount"; public static final String CURRENT_THREADS_BUSY = "currentThreadsBusy"; public static final String MAX_THREADS = "maxThreads"; public static final String POOL_SIZE = "poolSize"; public static final String CORE_POOL_SIZE = "corePoolSize"; public static final String QUEUE_SIZE = "queueSize"; private final ThreadPoolExecutor threadPoolExecutor; private long currentTask; private long lastTask; private long currentCompletedTask; private long lastCompletedTask; public ThreadPoolMeter(MeterRegistry meterRegistry, String threadPoolName, ThreadPoolExecutor threadPoolExecutor) { this.threadPoolExecutor = threadPoolExecutor; Gauge.builder(THREAD_POOL_METER, () -> currentTask) .tags(ID, threadPoolName, STAGE, TASK_COUNT) .register(meterRegistry); Gauge.builder(THREAD_POOL_METER, () -> currentCompletedTask) .tags(ID, threadPoolName, STAGE, COMPLETED_TASK_COUNT) .register(meterRegistry); Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getActiveCount) .tags(ID, threadPoolName, STAGE, CURRENT_THREADS_BUSY) .register(meterRegistry); Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getMaximumPoolSize) .tags(ID, threadPoolName, STAGE, MAX_THREADS) .register(meterRegistry); Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getPoolSize) .tags(ID, threadPoolName, STAGE, POOL_SIZE) .register(meterRegistry); Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getCorePoolSize) .tags(ID, threadPoolName, STAGE, CORE_POOL_SIZE) .register(meterRegistry); Gauge.builder(THREAD_POOL_METER, () -> threadPoolExecutor.getQueue().size()) .tags(ID, threadPoolName, STAGE, QUEUE_SIZE) .register(meterRegistry); if (threadPoolExecutor instanceof ThreadPoolExecutorEx) { Gauge.builder(THREAD_POOL_METER, () -> ((ThreadPoolExecutorEx) (threadPoolExecutor)).getRejectedCount()) .tags(ID, threadPoolName, STAGE, REJECTED_COUNT) .register(meterRegistry); } } @Override public void poll(long msNow, long secondInterval) { long temp = threadPoolExecutor.getTaskCount(); currentTask = temp - lastTask; lastTask = temp; temp = threadPoolExecutor.getCompletedTaskCount(); currentCompletedTask = temp - lastCompletedTask; lastCompletedTask = temp; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.vertx; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; public class EndpointMeter implements PeriodMeter { private static final double SNV_MILLI_SECONDS = 1.0 / TimeUnit.MILLISECONDS.toNanos(1L); public static final String ADDRESS = "address"; public static final String STATISTIC = "statistic"; public static final String CONNECT_COUNT = "connectCount"; public static final String DISCONNECT_COUNT = "disconnectCount"; public static final String CONNECTIONS = "connections"; public static final String BYTES_READ = "bytesRead"; public static final String BYTES_WRITTEN = "bytesWritten"; public static final String REQUESTS = "requests"; public static final String LATENCY = "latency"; protected DefaultEndpointMetric metric; protected final MeterRegistry meterRegistry; private final Gauge connectCount; private final Gauge disconnectCount; private final Gauge connections; private final Gauge bytesRead; private final Gauge bytesWritten; private final Gauge requests; private final Gauge latency; private long currentConnectCount; private long lastConnectCount; private long currentDisconnectCount; private long lastDisconnectCount; private long currentBytesRead; private long lastBytesRead; private long currentBytesWritten; private long lastBytesWritten; private long currentRequests; private long lastRequests; private long currentRequestsForLatency; private long lastRequestsForLatency; private long currentLatency; private long lastLatency; public EndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { this.meterRegistry = meterRegistry; this.metric = metric; tags = tags.and(Tag.of(ADDRESS, metric.getAddress())); connectCount = Gauge.builder(name, () -> currentConnectCount) .tags(tags.and(Tag.of(STATISTIC, CONNECT_COUNT))) .register(meterRegistry); disconnectCount = Gauge.builder(name, () -> currentDisconnectCount) .tags(tags.and(Tag.of(STATISTIC, DISCONNECT_COUNT))) .register(meterRegistry); connections = Gauge.builder(name, () -> this.metric.getConnectCount() - this.metric.getDisconnectCount()) .tags(tags.and(Tag.of(STATISTIC, CONNECTIONS))) .register(meterRegistry); bytesRead = Gauge.builder(name, () -> currentBytesRead) .tags(tags.and(Tag.of(STATISTIC, BYTES_READ))) .register(meterRegistry); bytesWritten = Gauge.builder(name, () -> currentBytesWritten) .tags(tags.and(Tag.of(STATISTIC, BYTES_WRITTEN))) .register(meterRegistry); requests = Gauge.builder(name, () -> currentRequests) .tags(tags.and(Tag.of(STATISTIC, REQUESTS))) .register(meterRegistry); latency = Gauge.builder(name, () -> currentRequestsForLatency == 0 ? 0 : (currentLatency) / ((double) (currentRequestsForLatency)) * SNV_MILLI_SECONDS) .tags(tags.and(Tag.of(STATISTIC, LATENCY))) .register(meterRegistry); } public DefaultEndpointMetric getMetric() { return metric; } public void destroy() { this.meterRegistry.remove(connectCount); this.meterRegistry.remove(disconnectCount); this.meterRegistry.remove(connections); this.meterRegistry.remove(bytesRead); this.meterRegistry.remove(bytesWritten); this.meterRegistry.remove(requests); this.meterRegistry.remove(latency); } @Override public void poll(long msNow, long secondInterval) { long temp = metric.getConnectCount(); currentConnectCount = temp - lastConnectCount; lastConnectCount = temp; temp = metric.getDisconnectCount(); currentDisconnectCount = temp - lastDisconnectCount; lastDisconnectCount = temp; temp = metric.getBytesRead(); currentBytesRead = temp - lastBytesRead; lastBytesRead = temp; temp = metric.getBytesWritten(); currentBytesWritten = temp - lastBytesWritten; lastBytesWritten = temp; temp = metric.getRequests(); currentRequests = temp - lastRequests; lastRequests = temp; temp = metric.getRequests(); currentRequestsForLatency = temp - lastRequestsForLatency; lastRequestsForLatency = temp; temp = metric.getLatency(); currentLatency = temp - lastLatency; lastLatency = temp; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.vertx; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; public class HttpClientEndpointMeter extends EndpointMeter { public static final String QUEUE_COUNT = "queueCount"; private final Gauge queueCount; private long currentQueueCount; public HttpClientEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { super(meterRegistry, name, tags, metric); queueCount = Gauge.builder(name, () -> currentQueueCount) .tags(tags.and(Tag.of(STATISTIC, QUEUE_COUNT), Tag.of(ADDRESS, metric.getAddress()))) .register(meterRegistry); } @Override public void destroy() { super.destroy(); this.meterRegistry.remove(queueCount); } @Override public void poll(long msNow, long secondInterval) { super.poll(msNow, secondInterval); currentQueueCount = ((DefaultClientEndpointMetric) metric).getQueueCount(); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.vertx; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class HttpClientEndpointsMeter extends VertxEndpointsMeter { public HttpClientEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultClientEndpointMetricManager clientEndpointMetricManager) { super(meterRegistry, name, tags, clientEndpointMetricManager.getClientEndpointMetricMap()); } @Override protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { return new HttpClientEndpointMeter(meterRegistry, name, tags, metric); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.vertx; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; public class ServerEndpointMeter extends EndpointMeter { public static final String REJECT_BY_CONNECTION_LIMIT = "rejectByConnectionLimit"; private final Gauge rejectByConnectionLimit; private long lastRejectByConnectionLimit; private long currentRejectByConnectionLimit; public ServerEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { super(meterRegistry, name, tags, metric); rejectByConnectionLimit = Gauge.builder(name, () -> currentRejectByConnectionLimit) .tags(tags.and(Tag.of(STATISTIC, REJECT_BY_CONNECTION_LIMIT), Tag.of(ADDRESS, metric.getAddress()))) .register(meterRegistry); } @Override public void destroy() { super.destroy(); this.meterRegistry.remove(rejectByConnectionLimit); } @Override public void poll(long msNow, long secondInterval) { super.poll(msNow, secondInterval); long current = ((DefaultServerEndpointMetric) metric).getRejectByConnectionLimitCount(); currentRejectByConnectionLimit = current - lastRejectByConnectionLimit; lastRejectByConnectionLimit = current; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.vertx; import java.util.Map; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class ServerEndpointsMeter extends VertxEndpointsMeter { public ServerEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, Map endpointMetricMap) { super(meterRegistry, name, tags, endpointMetricMap); } @Override protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { return new ServerEndpointMeter(meterRegistry, name, tags, metric); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.vertx; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; public class VertxEndpointsMeter implements PeriodMeter { private final Map endpointMetricMap; protected final MeterRegistry meterRegistry; protected final String name; protected final Tags tags; private final Map endpointMeterMap = new ConcurrentHashMapEx<>(); @SuppressWarnings("unchecked") public VertxEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, Map endpointMetricMap) { this.meterRegistry = meterRegistry; this.name = name; this.tags = tags; this.endpointMetricMap = (Map) endpointMetricMap; } private void syncMeters(long msNow, long secondInterval) { for (EndpointMeter meter : endpointMeterMap.values()) { if (!endpointMetricMap.containsKey(meter.getMetric().getAddress())) { EndpointMeter removed = endpointMeterMap.remove(meter.getMetric().getAddress()); removed.destroy(); } } for (DefaultEndpointMetric metric : endpointMetricMap.values()) { EndpointMeter updated = endpointMeterMap.computeIfAbsent(metric.getAddress(), address -> createEndpointMeter(metric)); updated.poll(msNow, secondInterval); } } protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { return new EndpointMeter(meterRegistry, name, tags, metric); } @Override public void poll(long msNow, long secondInterval) { syncMeters(msNow, secondInterval); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; public abstract class AbstractMeasurementNodeLogPublisher { protected StringBuilder sb; protected MeasurementNode measurementNode; private final boolean exists; public AbstractMeasurementNodeLogPublisher(MeasurementTree tree, StringBuilder sb, String... childNames) { this.sb = sb; measurementNode = tree.findChild(childNames); exists = measurementNode != null && !measurementNode.getMeasurements().isEmpty(); } public boolean isExists() { return exists; } public abstract void print(boolean printDetail); } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointMeter; public class ClientEndpointsLogPublisher extends AbstractMeasurementNodeLogPublisher { public ClientEndpointsLogPublisher(MeasurementTree tree, StringBuilder sb, String meterName) { super(tree, sb, VertxMetersInitializer.VERTX_ENDPOINTS, meterName); } @Override public void print(boolean printDetail) { if (!printDetail) { return; } appendLine(sb, " client.endpoints:"); appendLine(sb, " connectCount disconnectCount queue connections requests latency send(Bps) receive(Bps) remote"); List measurements = new ArrayList<>(measurementNode.getChildren().size()); measurements.addAll(measurementNode.getChildren().values()); measurements.sort(MeasurementNode::compareTo); for (MeasurementNode address : measurements) { appendLine(sb, " %-12.0f %-15.0f %-13.0f %-11.0f %-8.0f %-7.0f %-9s %-12s %s", address.findChild(EndpointMeter.CONNECT_COUNT).summary(), address.findChild(EndpointMeter.DISCONNECT_COUNT).summary(), address.findChild(HttpClientEndpointMeter.QUEUE_COUNT).summary(), address.findChild(EndpointMeter.CONNECTIONS).summary(), address.findChild(EndpointMeter.REQUESTS).summary(), address.findChild(EndpointMeter.LATENCY).summary(), NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_WRITTEN).summary()), NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_READ).summary()), address.getName() ); } } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.foundation.metrics.PolledEvent; import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig; import org.apache.servicecomb.foundation.metrics.meter.LatencyScopeConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; import org.apache.servicecomb.metrics.core.meter.os.NetMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; import org.apache.servicecomb.metrics.core.meter.os.SystemMeter; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.base.Strings; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.vertx.core.Vertx; public class DefaultLogPublisher implements MetricsInitializer { private static final Logger LOGGER = LoggerFactory.getLogger("scb-metrics"); public static final String ENABLED = "servicecomb.metrics.publisher.defaultLog.enabled"; // for a client, maybe will connect to too many endpoints, so default not print detail, just print summary public static final String ENDPOINTS_CLIENT_DETAIL_ENABLED = "servicecomb.metrics.publisher.defaultLog.endpoints.client.detail.enabled"; private static final String FIRST_LINE_SIMPLE_FORMAT = " %-11s\n"; private static final String SIMPLE_FORMAT = " %-8.1f %-18s %s%s\n"; //details private static final String PRODUCER_DETAILS_FORMAT = "" + " prepare: %-18s decode-request : %-18s queue : %-18s business-execute: %s\n" + " encode-response: %-18s send: %-18s\n"; private static final String CONSUMER_DETAILS_FORMAT = "" + " prepare : %-18s connection : %-18s encode-request: %-18s send : %s\n" + " wait : %-18s decode-response : %-18s\n"; private static final String EDGE_DETAILS_FORMAT = "" + " prepare : %-18s provider-decode : %-18s connection : %-18s consumer-encode : %s\n" + " consumer-send : %-18s wait : %-18s consumer-decode : %-18s provider-encode : %s\n" + " provider-send : %-18s\n"; private LatencyDistributionConfig latencyDistributionConfig; /** * if config is 0,1,10,100 then header will be:
* [0,1) [1,10) [10,100) [100,) */ private String latencyDistributionHeader = ""; /** * if config is 0,1,10,100 then format will be:
* %-7d %-7d %-9d %-7d */ private String latencyDistributionFormat = ""; private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { if (!environment.getProperty(ENABLED, boolean.class, false)) { return; } initLatencyDistribution(config); eventBus.register(this); } private void initLatencyDistribution(MetricsBootstrapConfig config) { // default length is 7 which include a space, one minute 999999 requests, TPS is 16666, mostly it's enough int leastLatencyScopeStrLength = config.getMinScopeLength(); latencyDistributionConfig = new LatencyDistributionConfig(config.getLatencyDistribution()); String header; for (LatencyScopeConfig scopeConfig : latencyDistributionConfig.getScopeConfigs()) { if (scopeConfig.getMsMax() == LatencyDistributionConfig.MAX_LATENCY) { header = String.format("[%d,) ", scopeConfig.getMsMin()); } else { header = String.format("[%d,%d) ", scopeConfig.getMsMin(), scopeConfig.getMsMax()); } header = Strings.padEnd(header, leastLatencyScopeStrLength, ' '); latencyDistributionHeader += header; String format = "%-" + (header.length() - 1) + "d "; latencyDistributionFormat += format; } } @Subscribe public void onPolledEvent(PolledEvent event) { try { printLog(event.getMeters()); } catch (Throwable e) { // make development easier LOGGER.error("Failed to print perf log.", e); } } protected void printLog(List meters) { StringBuilder sb = new StringBuilder(); sb.append("\n"); PublishModelFactory factory = new PublishModelFactory(meters); DefaultPublishModel model = factory.createDefaultPublishModel(); printOsLog(factory.getTree(), sb); printVertxMetrics(factory.getTree(), sb); printThreadPoolMetrics(model, sb); printConsumerLog(model, sb); printProducerLog(model, sb); printEdgeLog(model, sb); LOGGER.info(sb.toString()); } protected void printOsLog(MeasurementTree tree, StringBuilder sb) { MeasurementNode osNode = tree.findChild(OsMeter.OS_NAME); if (osNode == null || osNode.getMeasurements().isEmpty()) { return; } appendLine(sb, "os:"); printCpuLog(sb, osNode); printNetLog(sb, osNode); } private void printNetLog(StringBuilder sb, MeasurementNode osNode) { MeasurementNode netNode = osNode.findChild(OsMeter.OS_TYPE_NET); if (netNode == null || netNode.getMeasurements().isEmpty()) { return; } appendLine(sb, " net:"); appendLine(sb, " send(Bps) recv(Bps) send(pps) recv(pps) interface"); StringBuilder tmpSb = new StringBuilder(); for (MeasurementNode interfaceNode : netNode.getChildren().values()) { double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.getValue()).summary(); double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.getValue()).summary(); double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.getValue()).summary(); double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.getValue()).summary(); if (sendRate == 0 && receiveRate == 0 && receivePacketsRate == 0 && sendPacketsRate == 0) { continue; } appendLine(tmpSb, " %-12s %-12s %-12s %-12s %s", NetUtils.humanReadableBytes((long) sendRate), NetUtils.humanReadableBytes((long) receiveRate), NetUtils.humanReadableBytes((long) sendPacketsRate), NetUtils.humanReadableBytes((long) receivePacketsRate), interfaceNode.getName()); } if (tmpSb.length() != 0) { sb.append(tmpSb); } } private void printCpuLog(StringBuilder sb, MeasurementNode osNode) { MeasurementNode cpuNode = osNode.findChild(SystemMeter.CPU_USAGE); MeasurementNode processNode = osNode.findChild(SystemMeter.PROCESS_CPU_USAGE); MeasurementNode memoryNode = osNode.findChild(SystemMeter.MEMORY_USAGE); MeasurementNode slaNode = osNode.findChild(SystemMeter.SYSTEM_LOAD_AVERAGE); appendLine(sb, " cpu:"); appendLine(sb, " all usage: %.2f%% process usage: %.2f%% sla: %.2f memory usage: %.2f%%", cpuNode.summary() * 100, processNode.summary() * 100, slaNode.summary(), memoryNode.summary() * 100); } protected void printThreadPoolMetrics(DefaultPublishModel model, StringBuilder sb) { if (model.getThreadPools().isEmpty()) { return; } sb.append("threadPool:\n"); sb.append(" coreSize maxThreads poolSize currentBusy rejected queueSize taskCount taskFinished name\n"); for (Entry entry : model.getThreadPools().entrySet()) { ThreadPoolPublishModel threadPoolPublishModel = entry.getValue(); sb.append(String.format(" %-8d %-10d %-8d %-11d %-8.0f %-9d %-9.1f %-12.1f %s\n", threadPoolPublishModel.getCorePoolSize(), threadPoolPublishModel.getMaxThreads(), threadPoolPublishModel.getPoolSize(), threadPoolPublishModel.getCurrentThreadsBusy(), threadPoolPublishModel.getRejected(), threadPoolPublishModel.getQueueSize(), threadPoolPublishModel.getAvgTaskCount(), threadPoolPublishModel.getAvgCompletedTaskCount(), entry.getKey())); } } protected void printEdgeLog(DefaultPublishModel model, StringBuilder sb) { OperationPerfGroups edgePerf = model.getEdge().getOperationPerfGroups(); if (edgePerf == null) { return; } sb.append("" + "edge:\n" + " simple:\n" + " status requests latency ") .append(latencyDistributionHeader) .append("operation\n"); StringBuilder detailsBuilder = new StringBuilder(); //print sample for (Map statusMap : edgePerf.getGroups().values()) { for (OperationPerfGroup perfGroup : statusMap.values()) { //append sample sb.append(printSamplePerf(perfGroup)); //append details detailsBuilder.append(printEdgeDetailsPerf(perfGroup)); } } sb.append(" details:\n").append(detailsBuilder); } protected void printConsumerLog(DefaultPublishModel model, StringBuilder sb) { OperationPerfGroups consumerPerf = model.getConsumer().getOperationPerfGroups(); if (consumerPerf == null) { return; } sb.append("" + "consumer:\n" + " simple:\n" + " status requests latency ") .append(latencyDistributionHeader) .append("operation\n"); StringBuilder detailsBuilder = new StringBuilder(); //print sample for (Map statusMap : consumerPerf.getGroups().values()) { for (OperationPerfGroup perfGroup : statusMap.values()) { //append sample sb.append(printSamplePerf(perfGroup)); //append details detailsBuilder.append(printConsumerDetailsPerf(perfGroup)); } } sb.append(" details:\n").append(detailsBuilder); } protected void printProducerLog(DefaultPublishModel model, StringBuilder sb) { OperationPerfGroups producerPerf = model.getProducer().getOperationPerfGroups(); if (producerPerf == null) { return; } sb.append("" + "producer:\n" + " simple:\n" + " status requests latency ") .append(latencyDistributionHeader) .append("operation\n"); // use detailsBuilder, we can traverse the map only once StringBuilder detailsBuilder = new StringBuilder(); //print sample for (Map statusMap : producerPerf.getGroups().values()) { for (OperationPerfGroup perfGroup : statusMap.values()) { //append sample sb.append(printSamplePerf(perfGroup)); //append details detailsBuilder.append(printProducerDetailsPerf(perfGroup)); } } //print details sb.append(" details:\n").append(detailsBuilder); } private StringBuilder printSamplePerf(OperationPerfGroup perfGroup) { StringBuilder sb = new StringBuilder(); String status = perfGroup.getTransport() + "." + perfGroup.getStatus() + ":"; sb.append(String.format(FIRST_LINE_SIMPLE_FORMAT, status)); for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); if (isIgnoreEmptyPerf(operationPerf)) { continue; } PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); sb.append(String.format(SIMPLE_FORMAT, stageTotal.getTotalRequests(), getDetailsFromPerf(stageTotal), formatLatencyDistribution(operationPerf), operationPerf.getOperation())); } OperationPerf summaryOperation = perfGroup.getSummary(); PerfInfo stageSummaryTotal = summaryOperation.findStage(InvocationStageTrace.STAGE_TOTAL); //print summary sb.append(String.format(SIMPLE_FORMAT, stageSummaryTotal.getTotalRequests(), getDetailsFromPerf(stageSummaryTotal), formatLatencyDistribution(summaryOperation), "(summary)")); return sb; } private String formatLatencyDistribution(OperationPerf operationPerf) { return String.format(latencyDistributionFormat, (Object[]) operationPerf.getLatencyDistribution()); } private StringBuilder printProducerDetailsPerf(OperationPerfGroup perfGroup) { StringBuilder sb = new StringBuilder(); //append rest."200": sb.append(" ") .append(perfGroup.getTransport()) .append(".") .append(perfGroup.getStatus()) .append(":\n"); PerfInfo prepare, queue, providerDecode, providerEncode, execute, sendResp; for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { if (isIgnoreEmptyPerf(operationPerf)) { continue; } prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); queue = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_QUEUE); providerDecode = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST); providerEncode = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE); execute = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); sendResp = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_SEND); sb.append(" ") .append(operationPerf.getOperation()) .append(":\n") .append(String.format(PRODUCER_DETAILS_FORMAT, getDetailsFromPerf(prepare), getDetailsFromPerf(providerDecode), getDetailsFromPerf(queue), getDetailsFromPerf(execute), getDetailsFromPerf(providerEncode), getDetailsFromPerf(sendResp) )); } return sb; } private StringBuilder printConsumerDetailsPerf(OperationPerfGroup perfGroup) { StringBuilder sb = new StringBuilder(); //append rest."200": sb.append(" ") .append(perfGroup.getTransport()) .append(".") .append(perfGroup.getStatus()) .append(":\n"); PerfInfo prepare, encodeRequest, decodeResponse, sendReq, getConnect, waitResp; for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { if (isIgnoreEmptyPerf(operationPerf)) { continue; } prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); encodeRequest = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST); decodeResponse = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE); sendReq = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_SEND); getConnect = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_CONNECTION); waitResp = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_WAIT); sb.append(" ") .append(operationPerf.getOperation()) .append(":\n") .append(String.format(CONSUMER_DETAILS_FORMAT, getDetailsFromPerf(prepare), getDetailsFromPerf(getConnect), getDetailsFromPerf(encodeRequest), getDetailsFromPerf(sendReq), getDetailsFromPerf(waitResp), getDetailsFromPerf(decodeResponse) )); } return sb; } private StringBuilder printEdgeDetailsPerf(OperationPerfGroup perfGroup) { StringBuilder sb = new StringBuilder(); //append rest."200": sb.append(" ") .append(perfGroup.getTransport()) .append(".") .append(perfGroup.getStatus()) .append(":\n"); PerfInfo prepare, connection, decodeProviderRequest, encodeProviderResponse, encodeConsumerRequest, decodeConsumerResponse, sendReq, getConnect, waitResp, sendResp; for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { if (isIgnoreEmptyPerf(operationPerf)) { continue; } prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); connection = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_CONNECTION); decodeProviderRequest = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST); encodeProviderResponse = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE); encodeConsumerRequest = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST); sendReq = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_SEND); decodeConsumerResponse = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE); waitResp = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_WAIT); sendResp = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_SEND); sb.append(" ") .append(operationPerf.getOperation()) .append(":\n") .append(String.format(EDGE_DETAILS_FORMAT, getDetailsFromPerf(prepare), getDetailsFromPerf(decodeProviderRequest), getDetailsFromPerf(connection), getDetailsFromPerf(encodeConsumerRequest), getDetailsFromPerf(sendReq), getDetailsFromPerf(waitResp), getDetailsFromPerf(decodeConsumerResponse), getDetailsFromPerf(encodeProviderResponse), getDetailsFromPerf(sendResp) )); } return sb; } private boolean isIgnoreEmptyPerf(OperationPerf operationPerf) { PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); // max latency is calculated in ring algorithm, maybe not 0 if (Double.compare(0D, stageTotal.getTotalRequests()) == 0 && Double.compare(0D, stageTotal.getMsMaxLatency()) == 0) { return true; } return false; } protected void printVertxMetrics(MeasurementTree tree, StringBuilder sb) { appendLine(sb, "vertx:"); appendLine(sb, " instances:"); appendLine(sb, " name eventLoopContext-created"); for (Entry entry : VertxUtils.getVertxMap().entrySet()) { appendLine(sb, " %-10s %d", entry.getKey(), // TODO will be fixed by next vertx update.entry.getValue().getEventLoopContextCreatedCount() 0); } ClientEndpointsLogPublisher client = new ClientEndpointsLogPublisher(tree, sb, VertxMetersInitializer.ENDPOINTS_CLIENT); ServerEndpointsLogPublisher server = new ServerEndpointsLogPublisher(tree, sb, VertxMetersInitializer.ENDPOINTS_SERVER); if (client.isExists() || server.isExists()) { appendLine(sb, " transport:"); if (client.isExists()) { client.print(environment.getProperty(ENDPOINTS_CLIENT_DETAIL_ENABLED, boolean.class, true)); } if (server.isExists()) { server.print(true); } } } private static String getDetailsFromPerf(PerfInfo perfInfo) { String result = ""; if (perfInfo != null) { result = String.format("%.3f/%.3f", perfInfo.calcMsLatency(), perfInfo.getMsMaxLatency()); } return result; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import java.util.List; import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.meter.os.NetMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.swagger.invocation.InvocationType; import io.micrometer.core.instrument.Meter; public class PublishModelFactory { private final MeasurementTree tree; public PublishModelFactory(List meters) { tree = createMeasurementTree(meters); } protected MeasurementTree createMeasurementTree(List meters) { MeasurementGroupConfig groupConfig = createMeasurementGroupConfig(); MeasurementTree tree = new MeasurementTree(); tree.from(meters.iterator(), groupConfig); return tree; } public MeasurementTree getTree() { return tree; } protected MeasurementGroupConfig createMeasurementGroupConfig() { MeasurementGroupConfig groupConfig = new MeasurementGroupConfig(); groupConfig.addGroup(MeterInvocationConst.INVOCATION_NAME, MeterInvocationConst.TAG_ROLE, MeterInvocationConst.TAG_TRANSPORT, MeterInvocationConst.TAG_OPERATION, MeterInvocationConst.TAG_STATUS, MeterInvocationConst.TAG_TYPE, new DefaultTagFinder(MeterInvocationConst.TAG_STAGE, true)); groupConfig.addGroup(OsMeter.OS_NAME, OsMeter.OS_TYPE, new DefaultTagFinder(NetMeter.INTERFACE, true), new DefaultTagFinder(NetMeter.STATISTIC, true)); groupConfig.addGroup(VertxMetersInitializer.VERTX_ENDPOINTS, VertxMetersInitializer.ENDPOINTS_TYPE, EndpointMeter.ADDRESS, EndpointMeter.STATISTIC); groupConfig.addGroup(ThreadPoolMeter.THREAD_POOL_METER, ThreadPoolMeter.ID, ThreadPoolMeter.STAGE); return groupConfig; } protected OperationPerfGroups generateOperationPerfGroups(MeasurementTree tree, String invocationTypeName) { MeasurementNode node = tree.findChild(MeterInvocationConst.INVOCATION_NAME, invocationTypeName); if (node == null) { return null; } OperationPerfGroups groups = new OperationPerfGroups(); // group by transport for (MeasurementNode transportNode : node.getChildren().values()) { // group by operation for (MeasurementNode operationNode : transportNode.getChildren().values()) { // group by status for (MeasurementNode statusNode : operationNode.getChildren().values()) { PublishUtils.addOperationPerfGroups(groups, transportNode.getName(), operationNode.getName(), statusNode); } } } return groups; } public DefaultPublishModel createDefaultPublishModel() { DefaultPublishModel model = new DefaultPublishModel(); model.getConsumer() .setOperationPerfGroups(generateOperationPerfGroups(tree, InvocationType.CONSUMER.name())); model.getProducer() .setOperationPerfGroups(generateOperationPerfGroups(tree, InvocationType.PROVIDER.name())); model.getEdge() .setOperationPerfGroups(generateOperationPerfGroups(tree, MeterInvocationConst.EDGE_INVOCATION_NAME)); model.setThreadPools(ThreadPoolMonitorPublishModelFactory.create(tree)); return model; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; import io.micrometer.core.instrument.Statistic; public final class PublishUtils { private PublishUtils() { } public static PerfInfo createPerfInfo(MeasurementNode stageNode) { PerfInfo perfInfo = new PerfInfo(); perfInfo.setTotalRequests(stageNode.findChild(Statistic.COUNT.name()).summary()); perfInfo.setMsTotalTime(stageNode.findChild(Statistic.TOTAL_TIME.name()).summary() * 1000); perfInfo.setMsMaxLatency(stageNode.findChild(Statistic.MAX.name()).summary() * 1000); return perfInfo; } public static OperationPerf createOperationPerf(String operation, MeasurementNode statusNode) { OperationPerf operationPerf = new OperationPerf(); operationPerf.setOperation(operation); MeasurementNode stageNode = statusNode.findChild(MeterInvocationConst.TAG_STAGE); stageNode.getChildren().values().forEach(mNode -> { PerfInfo perfInfo = createPerfInfo(mNode); operationPerf.getStages().put(mNode.getName(), perfInfo); }); MeasurementNode latencyNode = statusNode.findChild(MeterInvocationConst.TAG_DISTRIBUTION); if (latencyNode != null && latencyNode.getMeasurements() != null) { operationPerf.setLatencyDistribution(latencyNode.getMeasurements().stream() .map(m -> (int) m.getValue()) .toArray(Integer[]::new)); } return operationPerf; } public static void addOperationPerfGroups(OperationPerfGroups operationPerfGroups, String transport, String operation, MeasurementNode statusNode) { Map statusMap = operationPerfGroups .getGroups() .computeIfAbsent(transport, tn -> new HashMap<>()); OperationPerfGroup group = statusMap .computeIfAbsent(statusNode.getName(), status -> new OperationPerfGroup(transport, status)); OperationPerf operationPerf = createOperationPerf(operation, statusNode); group.addOperationPerf(operationPerf); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointMeter; public class ServerEndpointsLogPublisher extends AbstractMeasurementNodeLogPublisher { public ServerEndpointsLogPublisher(MeasurementTree tree, StringBuilder sb, String meterName) { super(tree, sb, VertxMetersInitializer.VERTX_ENDPOINTS, meterName); } @Override public void print(boolean printDetail) { if (!printDetail) { return; } appendLine(sb, " server.endpoints:"); appendLine(sb, " connectCount disconnectCount rejectByLimit connections requests latency send(Bps) receive(Bps) listen"); for (MeasurementNode address : measurementNode.getChildren().values()) { if (printDetail) { appendLine(sb, " %-12.0f %-15.0f %-13.0f %-11.0f %-8.0f %-7.0f %-9s %-12s %s", address.findChild(EndpointMeter.CONNECT_COUNT).summary(), address.findChild(EndpointMeter.DISCONNECT_COUNT).summary(), address.findChild(ServerEndpointMeter.REJECT_BY_CONNECTION_LIMIT).summary(), address.findChild(EndpointMeter.CONNECTIONS).summary(), address.findChild(EndpointMeter.REQUESTS).summary(), address.findChild(EndpointMeter.LATENCY).summary(), NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_WRITTEN).summary()), NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_READ).summary()), address.getName() ); } } } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_CONNECTION; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_SEND; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_WAIT; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PREPARE; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_BUSINESS; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_QUEUE; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_SEND; import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_TOTAL; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.OperationConfig; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.Subscribe; public class SlowInvocationLogger { private static final Logger LOGGER = LoggerFactory.getLogger("scb-slow"); public SlowInvocationLogger(SCBEngine scbEngine) { scbEngine.getEventBus().register(this); } @Subscribe @AllowConcurrentEvents public void onInvocationFinish(InvocationFinishEvent event) { Invocation invocation = event.getInvocation(); OperationConfig operationConfig = invocation.getOperationMeta().getConfig(); if (!operationConfig.isSlowInvocationEnabled() || invocation.getInvocationStageTrace().calcTotal() < operationConfig.getNanoSlowInvocation()) { return; } if (invocation.isProducer()) { logSlowProvider(invocation, event.getResponse(), operationConfig); return; } if (invocation.isEdge()) { logSlowEdge(invocation, event.getResponse(), operationConfig); return; } logSlowConsumer(invocation, event.getResponse(), operationConfig); } private static String collectClientAddress(Invocation invocation) { HttpServletRequestEx requestEx = invocation.getRequestEx(); return requestEx == null ? "unknown" : requestEx.getRemoteAddr() + ":" + requestEx.getRemotePort(); } private static String collectTargetAddress(Invocation invocation) { Endpoint endpoint = invocation.getEndpoint(); return endpoint == null ? "unknown" : endpoint.getEndpoint(); } private static String formatTime(long doubleNano) { long micros = TimeUnit.NANOSECONDS.toMicros(doubleNano); return micros / 1000 + "." + micros % 1000; } private static void logSlowProvider(Invocation invocation, Response response, OperationConfig operationConfig) { RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); StringBuilder sb = new StringBuilder(); sb.append("Slow Provider invocation [").append(invocation.getInvocationQualifiedName()) .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") .append(")[").append(invocation.getTraceId()).append("]\n") .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) .append(formatPair(" ", "endpoint", collectClientAddress(invocation))) .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) .append(formatPair(" ", STAGE_PROVIDER_DECODE_REQUEST, stageTrace.calcProviderDecodeRequest())) .append(formatPair(" ", STAGE_PROVIDER_QUEUE, stageTrace.calcQueue())) .append(formatPair(" ", STAGE_PROVIDER_BUSINESS, stageTrace.calcBusinessExecute())) .append(formatPair(" ", STAGE_PROVIDER_ENCODE_RESPONSE, stageTrace.calcProviderEncodeResponse())) .append(formatPair(" ", STAGE_PROVIDER_SEND, stageTrace.calcProviderSendResponse())); List sorted = new ArrayList<>(stageTrace.getStages().keySet()); sorted.stream().sorted().forEach(key -> { sb.append(formatPair(" ", key, InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), stageTrace.getStages().get(key).getBeginTime()))); }); LOGGER.warn(sb.toString()); } protected static String formatPair(String padding, String name, String value) { return String.format("%-20s: %20s\n", padding + name, value); } protected static String formatPair(String padding, String name, long time) { return String.format("%-20s: %8sms\n", padding + name, formatTime(time)); } private static void logSlowConsumer(Invocation invocation, Response response, OperationConfig operationConfig) { RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); StringBuilder sb = new StringBuilder(); sb.append("Slow Consumer invocation [").append(invocation.getInvocationQualifiedName()) .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") .append(")[").append(invocation.getTraceId()).append("]\n") .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) .append(formatPair(" ", "endpoint", collectTargetAddress(invocation))) .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) .append(formatPair(" ", STAGE_CONSUMER_CONNECTION, stageTrace.calcConnection())) .append(formatPair(" ", STAGE_CONSUMER_ENCODE_REQUEST, stageTrace.calcConsumerEncodeRequest())) .append(formatPair(" ", STAGE_CONSUMER_SEND, stageTrace.calcConsumerSendRequest())) .append(formatPair(" ", STAGE_CONSUMER_WAIT, stageTrace.calcWait())) .append(formatPair(" ", STAGE_CONSUMER_DECODE_RESPONSE, stageTrace.calcConsumerDecodeResponse())); List sorted = new ArrayList<>(stageTrace.getStages().keySet()); sorted.stream().sorted().forEach(key -> { sb.append(formatPair(" ", key, InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), stageTrace.getStages().get(key).getBeginTime()))); }); LOGGER.warn(sb.toString()); } private static void logSlowEdge(Invocation invocation, Response response, OperationConfig operationConfig) { RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); StringBuilder sb = new StringBuilder(); sb.append("Slow Edge invocation [").append(invocation.getInvocationQualifiedName()) .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") .append(")[").append(invocation.getTraceId()).append("]\n") .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) .append(formatPair(" ", "endpoint", collectTargetAddress(invocation))) .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) .append(formatPair(" ", STAGE_PROVIDER_DECODE_REQUEST, stageTrace.calcProviderDecodeRequest())) .append(formatPair(" ", STAGE_CONSUMER_CONNECTION, stageTrace.calcConnection())) .append(formatPair(" ", STAGE_CONSUMER_ENCODE_REQUEST, stageTrace.calcConsumerEncodeRequest())) .append(formatPair(" ", STAGE_CONSUMER_SEND, stageTrace.calcConsumerSendRequest())) .append(formatPair(" ", STAGE_CONSUMER_WAIT, stageTrace.calcWait())) .append(formatPair(" ", STAGE_CONSUMER_DECODE_RESPONSE, stageTrace.calcConsumerDecodeResponse())) .append(formatPair(" ", STAGE_PROVIDER_ENCODE_RESPONSE, stageTrace.calcProviderEncodeResponse())) .append(formatPair(" ", STAGE_PROVIDER_SEND, stageTrace.calcProviderSendResponse())) ; List sorted = new ArrayList<>(stageTrace.getStages().keySet()); sorted.stream().sorted().forEach(key -> { sb.append(formatPair(" ", key, InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), stageTrace.getStages().get(key).getBeginTime()))); }); LOGGER.warn(sb.toString()); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; public class ConsumerPublishModel { private OperationPerfGroups operationPerfGroups; public OperationPerfGroups getOperationPerfGroups() { return operationPerfGroups; } public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) { this.operationPerfGroups = operationPerfGroups; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model; import java.util.HashMap; import java.util.Map; public class DefaultPublishModel { private ConsumerPublishModel consumer = new ConsumerPublishModel(); private ProducerPublishModel producer = new ProducerPublishModel(); private EdgePublishModel edge = new EdgePublishModel(); private Map threadPools = new HashMap<>(); public ConsumerPublishModel getConsumer() { return consumer; } public void setConsumer(ConsumerPublishModel consumer) { this.consumer = consumer; } public ProducerPublishModel getProducer() { return producer; } public EdgePublishModel getEdge() { return edge; } public void setEdge(EdgePublishModel edge) { this.edge = edge; } public void setProducer(ProducerPublishModel producer) { this.producer = producer; } public Map getThreadPools() { return threadPools; } public void setThreadPools(Map threadPools) { this.threadPools = threadPools; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/EdgePublishModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; public class EdgePublishModel extends ConsumerPublishModel { private OperationPerfGroups operationPerfGroups; public OperationPerfGroups getOperationPerfGroups() { return operationPerfGroups; } public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) { this.operationPerfGroups = operationPerfGroups; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; public class ProducerPublishModel { private OperationPerfGroups operationPerfGroups; public OperationPerfGroups getOperationPerfGroups() { return operationPerfGroups; } public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) { this.operationPerfGroups = operationPerfGroups; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ThreadPoolPublishModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model; public class ThreadPoolPublishModel { private double avgTaskCount; private double avgCompletedTaskCount; private int currentThreadsBusy; private int maxThreads; private int poolSize; private int corePoolSize; private int queueSize; private double rejected = Double.NaN; public double getAvgTaskCount() { return avgTaskCount; } public void setAvgTaskCount(double avgTaskCount) { this.avgTaskCount = avgTaskCount; } public double getAvgCompletedTaskCount() { return avgCompletedTaskCount; } public void setAvgCompletedTaskCount(double avgCompletedTaskCount) { this.avgCompletedTaskCount = avgCompletedTaskCount; } public int getCurrentThreadsBusy() { return currentThreadsBusy; } public void setCurrentThreadsBusy(int currentThreadsBusy) { this.currentThreadsBusy = currentThreadsBusy; } public int getMaxThreads() { return maxThreads; } public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; } public int getPoolSize() { return poolSize; } public void setPoolSize(int poolSize) { this.poolSize = poolSize; } public int getCorePoolSize() { return corePoolSize; } public void setCorePoolSize(int corePoolSize) { this.corePoolSize = corePoolSize; } public int getQueueSize() { return queueSize; } public void setQueueSize(int queueSize) { this.queueSize = queueSize; } public double getRejected() { return rejected; } public void setRejected(double rejected) { this.rejected = rejected; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class OperationPerf { private String operation; private Map stages = new HashMap<>(); private Integer[] latencyDistribution; public String getOperation() { return operation; } public void setOperation(String operation) { this.operation = operation; } public Map getStages() { return stages; } public Integer[] getLatencyDistribution() { return latencyDistribution; } public void setLatencyDistribution(Integer[] latencyDistribution) { this.latencyDistribution = latencyDistribution; } public void setStages(Map stages) { this.stages = stages; } public PerfInfo findStage(String stage) { return stages.get(stage); } public void add(OperationPerf operationPerf) { operationPerf.stages.forEach((key, value) -> { PerfInfo perfInfo = stages.computeIfAbsent(key, n -> new PerfInfo()); perfInfo.add(value); }); if (operationPerf.getLatencyDistribution() == null) { return; } if (latencyDistribution == null) { latencyDistribution = new Integer[operationPerf.getLatencyDistribution().length]; Arrays.fill(latencyDistribution, 0); } for (int idx = 0; idx < operationPerf.getLatencyDistribution().length; idx++) { latencyDistribution[idx] += operationPerf.getLatencyDistribution()[idx]; } } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import java.util.ArrayList; import java.util.List; public class OperationPerfGroup { private final String transport; private final String status; private final List operationPerfs = new ArrayList<>(); private OperationPerf summary; public OperationPerfGroup(String transport, String status) { this.transport = transport; this.status = status; } public String getTransport() { return transport; } public String getStatus() { return status; } public List getOperationPerfs() { return operationPerfs; } public OperationPerf getSummary() { return summary; } public void addOperationPerf(OperationPerf operationPerf) { operationPerfs.add(operationPerf); if (summary == null) { summary = new OperationPerf(); summary.setOperation(""); } summary.add(operationPerf); } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import java.util.HashMap; import java.util.Map; public class OperationPerfGroups { // first key: transport // second key: statusCode private Map> groups = new HashMap<>(); public Map> getGroups() { return groups; } public void setGroups(Map> groups) { this.groups = groups; } } ================================================ FILE: metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; public class PerfInfo { private double totalRequests; private double msTotalTime; private double msMaxLatency; public double getTotalRequests() { return totalRequests; } public void setTotalRequests(double totalRequests) { this.totalRequests = totalRequests; } public double getMsTotalTime() { return msTotalTime; } public void setMsTotalTime(double msTotalTime) { this.msTotalTime = msTotalTime; } public double getMsMaxLatency() { return msMaxLatency; } public void setMsMaxLatency(double msMaxLatency) { this.msMaxLatency = msMaxLatency; } public void add(PerfInfo other) { totalRequests += other.totalRequests; msTotalTime += other.msTotalTime; if (msMaxLatency < other.msMaxLatency) { msMaxLatency = other.msMaxLatency; } } public double calcMsLatency() { return (totalRequests != 0) ? msTotalTime / totalRequests : 0; } @Override public String toString() { return "PerfInfo [tps=" + totalRequests + ", msTotalTime=" + msTotalTime + ", msLatency=" + calcMsLatency() + ", msMaxLatency=" + msMaxLatency + "]"; } } ================================================ FILE: metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.metrics.core.MetricsCoreConfiguration ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import java.util.List; import org.junit.jupiter.api.Assertions; import io.micrometer.core.instrument.Measurement; public class AssertUtil { public static void assertMeasure(List measurements, int index, String expected) { Assertions.assertEquals(String.format("Measurement{%s}", expected), measurements.get(index).toString()); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import java.util.List; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; @ExtendWith(MockitoExtension.class) public class TestInvocationMetersInitializer { EventBus eventBus = new EventBus(); MeterRegistry registry = new SimpleMeterRegistry(); InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer(); @Mock Invocation invocation; Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setup() { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); invocationMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment)); } @Test public void consumerInvocation() { InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); Mockito.when(invocation.isConsumer()).thenReturn(true); Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.CONSUMER); Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); Mockito.when(invocationStageTrace.calcConnection()).thenReturn(9L); Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(4L); Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(5L); Mockito.when(invocationStageTrace.calcWait()).thenReturn(9L); Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(9L); Mockito.when(event.getInvocation()).thenReturn(invocation); Response mockResponse = Mockito.spy(Response.class); Mockito.when(event.getResponse()).thenReturn(mockResponse); Mockito.doReturn(0).when(mockResponse).getStatusCode(); eventBus.post(event); eventBus.post(event); MeasurementTree tree = new MeasurementTree(); tree.from(registry.getMeters().iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.0E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=5.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=8.0E-9"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=4.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); } @Test public void edgeInvocation() { InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.EDGE); Mockito.when(invocation.isEdge()).thenReturn(true); Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); Mockito.when(invocationStageTrace.calcProviderDecodeRequest()).thenReturn(9L); Mockito.when(invocationStageTrace.calcConnection()).thenReturn(9L); Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(4L); Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(5L); Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(8L); Mockito.when(invocationStageTrace.calcWait()).thenReturn(9L); Mockito.when(invocationStageTrace.calcProviderEncodeResponse()).thenReturn(9L); Mockito.when(invocationStageTrace.calcProviderSendResponse()).thenReturn(9L); Mockito.when(event.getInvocation()).thenReturn(invocation); Response mockResponse = Mockito.spy(Response.class); Mockito.when(event.getResponse()).thenReturn(mockResponse); Mockito.doReturn(0).when(mockResponse).getStatusCode(); eventBus.post(event); eventBus.post(event); MeasurementTree tree = new MeasurementTree(); tree.from(registry.getMeters().iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.0E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=5.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=8.0E-9"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=4.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.6E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=8.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); } @Test public void producerInvocation() { InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); Mockito.when(invocation.isConsumer()).thenReturn(false); Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.PROVIDER); Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); Mockito.when(invocationStageTrace.calcProviderDecodeRequest()).thenReturn(9L); Mockito.when(invocationStageTrace.calcQueue()).thenReturn(9L); Mockito.when(invocationStageTrace.calcBusinessExecute()).thenReturn(9L); Mockito.when(invocationStageTrace.calcProviderEncodeResponse()).thenReturn(9L); Mockito.when(invocationStageTrace.calcProviderSendResponse()).thenReturn(9L); Mockito.when(event.getInvocation()).thenReturn(invocation); Response mockResponse = Mockito.spy(Response.class); Mockito.when(event.getResponse()).thenReturn(mockResponse); Mockito.doReturn(0).when(mockResponse).getStatusCode(); eventBus.post(event); eventBus.post(event); MeasurementTree tree = new MeasurementTree(); tree.from(registry.getMeters().iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "queue") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "execute") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send") .getMeasurements(); Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, "statistic='MAX', value=9.0E-9"); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import java.io.File; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.os.NetMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; import org.apache.servicecomb.metrics.core.meter.os.SystemMeter; import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import com.google.common.eventbus.EventBus; import com.sun.management.OperatingSystemMXBean; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class TestOsMeterInitializer { MeterRegistry registry = new SimpleMeterRegistry(); @Mock EventBus eventBus; @Test public void init() { List list = new ArrayList<>(); list.add("13 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { fileUtilsMockedStatic.when(() -> { FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(list); OsMetersInitializer osMetersInitializer = new OsMetersInitializer(); osMetersInitializer.init(registry, eventBus, null); OsMeter osMeter = osMetersInitializer.getOsMeter(); SystemMeter systemMeter = osMeter.getCpuMeter(); OperatingSystemMXBean osBean = Mockito.mock(OperatingSystemMXBean.class); Mockito.when(osBean.getCpuLoad()).thenReturn(3.2D); Mockito.when(osBean.getProcessCpuLoad()).thenReturn(1.2D); systemMeter.setOsBean(osBean); NetMeter netMeter = osMeter.getNetMeter(); netMeter.setOsLinux(true); osMetersInitializer.poll(System.currentTimeMillis(), 1000); MeasurementTree tree = new MeasurementTree(); MeasurementGroupConfig group = new MeasurementGroupConfig(); group.addGroup(OsMeter.OS_NAME, OsMeter.OS_TYPE); tree.from(registry.getMeters().iterator(), group); Assertions.assertEquals(1.2D, tree.findChild(OsMeter.OS_NAME, SystemMeter.PROCESS_CPU_USAGE).summary(), 0.0); Assertions.assertEquals(3.2D, tree.findChild(OsMeter.OS_NAME, SystemMeter.CPU_USAGE).summary(), 0.0); Map interfaceInfoMap = netMeter.getInterfaceUsageMap(); Assertions.assertEquals(1, interfaceInfoMap.size()); InterfaceUsage eth0 = interfaceInfoMap.get("eth0"); // recv Bps Assertions.assertEquals(0L, eth0.getReceive().getLastValue()); Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0); Assertions.assertEquals(0, eth0.getReceive().getIndex()); // send Bps Assertions.assertEquals(0L, eth0.getSend().getLastValue()); Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0); Assertions.assertEquals(8, eth0.getSend().getIndex()); // recv pps Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue()); Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue()); Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); } } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.RunnableScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.executor.GroupExecutor; import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestThreadPoolMetersInitializer { MeterRegistry registry = new SimpleMeterRegistry(); ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer(); ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class); @Mocked BlockingQueue queue; @Mocked GroupExecutor groupExecutor; ExecutorService executor = Mockito.mock(ExecutorService.class); @Mocked ApplicationContext applicationContext; @Mocked MicroserviceMeta microserviceMeta; @Mocked OperationMeta operationMetaExecutor; @Mocked OperationMeta operationMetaSameExecutor; @Mocked OperationMeta operationMetaFixedThreadExecutor; @Test public void init() { new Expectations(SCBEngine.class) { { SCBEngine.getInstance().getProducerMicroserviceMeta(); result = microserviceMeta; } }; Map beanExecutors = new HashMap<>(); beanExecutors.put("executor", executor); beanExecutors.put("groupExecutor", groupExecutor); beanExecutors.put("threadPoolExecutor", threadPoolExecutor); new Expectations(BeanUtils.class) { { BeanUtils.getContext(); result = applicationContext; applicationContext.getBeansOfType(Executor.class); result = beanExecutors; } }; Mockito.when(threadPoolExecutor.getQueue()).thenReturn(queue); new Expectations() { { microserviceMeta.getOperations(); result = Arrays.asList(operationMetaExecutor, operationMetaSameExecutor, operationMetaFixedThreadExecutor); operationMetaExecutor.getExecutor(); result = executor; operationMetaSameExecutor.getExecutor(); result = executor; operationMetaFixedThreadExecutor.getExecutor(); result = groupExecutor; groupExecutor.getExecutorList(); result = Arrays.asList(threadPoolExecutor); queue.size(); result = 10d; } }; new MockUp() { @Mock void delayedExecute(RunnableScheduledFuture task) { } }; threadPoolMetersInitializer.init(registry, null, null); MeasurementTree tree = new MeasurementTree(); MeasurementGroupConfig group = new MeasurementGroupConfig(); group.addGroup(ThreadPoolMeter.THREAD_POOL_METER, ThreadPoolMeter.ID, ThreadPoolMeter.STAGE); tree.from(registry.getMeters().iterator(), group); MeasurementNode node = tree.findChild(ThreadPoolMeter.THREAD_POOL_METER); Assertions.assertEquals(node.findChild("groupExecutor-group0", "maxThreads").summary(), 0, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "rejectedCount").summary(), 0, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "completedTaskCount").summary(), 0, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "currentThreadsBusy").summary(), 0, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "corePoolSize").summary(), 0, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "poolSize").summary(), 0, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "queueSize").summary(), 10, 0); Assertions.assertEquals(node.findChild("groupExecutor-group0", "taskCount").summary(), 0, 0); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import static org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher.ENDPOINTS_CLIENT_DETAIL_ENABLED; import java.util.List; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.PolledEvent; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; import io.vertx.core.Future; import io.vertx.core.Promise; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServer; import io.vertx.core.impl.SysProps; import io.vertx.ext.web.Router; public class TestVertxMetersInitializer { MeterRegistry registry = new SimpleMeterRegistry(); EventBus eventBus = new EventBus(); VertxMetersInitializer vertxMetersInitializer = new VertxMetersInitializer(); DefaultLogPublisher logPublisher = new DefaultLogPublisher(); Environment environment = Mockito.mock(Environment.class); LogCollector logCollector = new LogCollector(); static HttpClient client; static HttpServer server; static int port; static String body = "body"; public static class TestServerVerticle extends AbstractVerticle { @Override public void start(Promise startPromise) { Router mainRouter = Router.router(vertx); mainRouter.route("/").handler(context -> context.response().end(body)); server = vertx.createHttpServer(); server.requestHandler(mainRouter); Future future = server.listen(0, "0.0.0.0"); future.onComplete((s, f) -> { if (f == null) { port = s.actualPort(); startPromise.complete(); return; } startPromise.fail(f); }); } } public static class TestClientVerticle extends AbstractVerticle { @Override public void start(Promise startPromise) { client = vertx.createHttpClient(); Future future = client.request(HttpMethod.GET, port, "127.0.0.1", "/"); future.onComplete((s, f) -> { if (f == null) { Future responseFuture = s.send(body); responseFuture.onComplete((rs, rf) -> { if (rf == null) { rs.bodyHandler((buffer) -> startPromise.complete()); } else { startPromise.fail(f); } }); } }); } } @BeforeEach public void setup() { Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); HttpClients.load(); } @AfterEach public void tearDown() { logCollector.clear(); HttpClients.destroy(); if (client != null) { client.shutdown(); } if (server != null) { server.shutdown(); } } @Test public void init() throws InterruptedException { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); VertxUtils .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestServerVerticle.class, new DeploymentOptions()); VertxUtils .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestClientVerticle.class, new DeploymentOptions()); vertxMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment)); logPublisher.setEnvironment(environment); logPublisher.init(registry, eventBus, null); vertxMetersInitializer.poll(0, 1); List meters = registry.getMeters(); testLog(logCollector, meters, true); logCollector.clear(); testLog(logCollector, meters, false); } private void testLog(LogCollector logCollector, List meters, boolean printDetail) { Mockito.when(environment.getProperty(ENDPOINTS_CLIENT_DETAIL_ENABLED, boolean.class, true)).thenReturn(printDetail); logPublisher.onPolledEvent(new PolledEvent(meters)); StringBuilder sb = new StringBuilder(); logCollector.getEvents().forEach(event -> sb.append(event.getMessage()).append("\n")); String actual = sb.toString(); int idx = actual.indexOf("vertx:\n"); actual = actual.substring(idx); String clientLatency; String serverLatency; String expect = "vertx:\n" + " instances:\n" + " name eventLoopContext-created\n" + " transport 0\n" + " transport:\n"; int clientLatencyIndex = actual.indexOf("1 0 0 1 1 ") + "1 0 0 1 1 ".length(); clientLatency = actual.substring(clientLatencyIndex, actual.indexOf(" ", clientLatencyIndex)); int serverLatencyIndex = actual.lastIndexOf("1 0 0 1 1 ") + "1 0 0 1 1 ".length(); serverLatency = actual.substring(serverLatencyIndex, actual.indexOf(" ", serverLatencyIndex)); int portSize = String.valueOf(port).length(); // in new vert.x version, bytes written must be higher than 4K or will be zero if (printDetail) { expect = expect + " client.endpoints:\n" + " connectCount disconnectCount queue connections requests latency send(Bps) receive(Bps) remote\n"; expect += " 1 0 0 1 1 %-7s 4 4 http://127.0.0.1:%-" + portSize + "s\n"; } expect += "" + " server.endpoints:\n" + " connectCount disconnectCount rejectByLimit connections requests latency send(Bps) receive(Bps) listen\n" + " 1 0 0 1 1 %-7s 4 4 0.0.0.0:0\n\n"; if (printDetail) { expect = String .format(expect, clientLatency, port, serverLatency); } else { expect = String.format(expect, serverLatency); } Assertions.assertEquals(expect, actual); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os; import static org.apache.servicecomb.metrics.core.meter.os.OsMeter.OS_TYPE; import static org.apache.servicecomb.metrics.core.meter.os.OsMeter.OS_TYPE_NET; import java.io.File; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; import org.junit.jupiter.api.Assertions; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; public class TestNetMeter { @Test public void testNetRefreshUnchanged() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { fileUtilsMockedStatic.when(() -> { FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(list); MeterRegistry meterRegistry = new SimpleMeterRegistry(); NetMeter netMeter = new NetMeter(meterRegistry, "os", Tags.of(OS_TYPE, OS_TYPE_NET)); list.remove(2); list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); netMeter.refreshNet(1); Map meterInterfaceInfoMap = netMeter.getInterfaceUsageMap(); Assertions.assertTrue(meterInterfaceInfoMap.containsKey("eth0")); InterfaceUsage eth0 = meterInterfaceInfoMap.get("eth0"); Assertions.assertEquals("eth0", eth0.getName()); // recv Bps Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); Assertions.assertEquals(0, eth0.getReceive().getIndex()); // send Bps Assertions.assertEquals(1L, eth0.getSend().getLastValue()); Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); Assertions.assertEquals(8, eth0.getSend().getIndex()); // recv pps Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); // measurement tree test MeasurementGroupConfig group = new MeasurementGroupConfig(); group.addGroup(OsMeter.OS_NAME, OS_TYPE, new DefaultTagFinder(NetMeter.INTERFACE, true), new DefaultTagFinder(NetMeter.STATISTIC, true)); MeasurementTree tree = new MeasurementTree(); tree.from(meterRegistry.getMeters().iterator(), group); MeasurementNode osNode = tree.findChild(OsMeter.OS_NAME); MeasurementNode netNode = osNode.findChild(OS_TYPE_NET); Assertions.assertEquals(1, netNode.getChildren().size()); for (MeasurementNode interfaceNode : netNode.getChildren().values()) { double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.getValue()).summary(); double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.getValue()).summary(); double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.getValue()).summary(); double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.getValue()).summary(); Assertions.assertEquals(1, sendRate, 0); Assertions.assertEquals(1, sendPacketsRate, 0); Assertions.assertEquals(1, receiveRate, 0); Assertions.assertEquals(1, receivePacketsRate, 0); } } } @Test public void testNetRefreshAdd() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { fileUtilsMockedStatic.when(() -> { FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(list); MeterRegistry meterRegistry = new SimpleMeterRegistry(); NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); netMeter.setOsLinux(true); netMeter.poll(0, 0); Map netMap = netMeter.getInterfaceUsageMap(); Assertions.assertEquals(1, netMap.size()); list.remove(2); list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); netMeter.refreshNet(1); Assertions.assertEquals(2, netMap.size()); InterfaceUsage eth0 = netMap.get("eth0"); Assertions.assertEquals("eth0", eth0.getName()); // recv Bps Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); Assertions.assertEquals(0, eth0.getReceive().getIndex()); // send Bps Assertions.assertEquals(1L, eth0.getSend().getLastValue()); Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); Assertions.assertEquals(8, eth0.getSend().getIndex()); // recv pps Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); InterfaceUsage lo = netMap.get("lo"); Assertions.assertEquals("lo", lo.getName()); // recv Bps Assertions.assertEquals(0L, lo.getReceive().getLastValue()); Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0); Assertions.assertEquals(0, lo.getReceive().getIndex()); // send Bps Assertions.assertEquals(0L, lo.getSend().getLastValue()); Assertions.assertEquals(0, lo.getSend().getRate(), 0.0); Assertions.assertEquals(8, lo.getSend().getIndex()); // recv pps Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue()); Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, lo.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue()); Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, lo.getPacketsSend().getIndex()); } } @Test public void testNetRefreshRemove() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { fileUtilsMockedStatic.when(() -> { FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(list); MeterRegistry meterRegistry = new SimpleMeterRegistry(); NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); netMeter.setOsLinux(true); netMeter.poll(0, 1); Map netMap = netMeter.getInterfaceUsageMap(); Assertions.assertEquals(2, netMap.size()); InterfaceUsage lo = netMap.get("lo"); InterfaceUsage eth0 = netMap.get("eth0"); Assertions.assertEquals("lo", lo.getName()); // recv Bps Assertions.assertEquals(0L, lo.getReceive().getLastValue()); Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0); Assertions.assertEquals(0, lo.getReceive().getIndex()); // send Bps Assertions.assertEquals(0L, lo.getSend().getLastValue()); Assertions.assertEquals(0, lo.getSend().getRate(), 0.0); Assertions.assertEquals(8, lo.getSend().getIndex()); // recv pps Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue()); Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, lo.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue()); Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, lo.getPacketsSend().getIndex()); Assertions.assertEquals("eth0", eth0.getName()); // recv Bps Assertions.assertEquals(0L, eth0.getReceive().getLastValue()); Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0); Assertions.assertEquals(0, eth0.getReceive().getIndex()); // send Bps Assertions.assertEquals(0L, eth0.getSend().getLastValue()); Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0); Assertions.assertEquals(8, eth0.getSend().getIndex()); // recv pps Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue()); Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue()); Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); list.remove(2); list.remove(2); list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); netMeter.refreshNet(1); Assertions.assertEquals("eth0", eth0.getName()); // recv Bps Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); Assertions.assertEquals(0, eth0.getReceive().getIndex()); // send Bps Assertions.assertEquals(1L, eth0.getSend().getLastValue()); Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); Assertions.assertEquals(8, eth0.getSend().getIndex()); // recv pps Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); // send pps Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); } } @Test public void testCalcMeasurements() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); MeterRegistry meterRegistry = new SimpleMeterRegistry(); try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { fileUtilsMockedStatic.when(() -> { FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(list); NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); netMeter.setOsLinux(true); list.remove(2); list.add("eth0: 3 1 0 0 0 0 0 1 3 1 1 0 0 0 0 0"); netMeter.poll(0, 1); MeasurementTree tree = new MeasurementTree(); tree.from(meterRegistry.getMeters().iterator(), new MeasurementGroupConfig("net", "statistic")); Measurement receive = tree.findChild("net", "receive").getMeasurements().get(0); Measurement send = tree.findChild("net", "send").getMeasurements().get(0); Measurement receivePackets = tree.findChild("net", "receivePackets").getMeasurements().get(0); Measurement sendPackets = tree.findChild("net", "sendPackets").getMeasurements().get(0); Assertions.assertEquals(3.0, send.getValue(), 0.0); Assertions.assertEquals(1.0, sendPackets.getValue(), 0.0); Assertions.assertEquals(3.0, receive.getValue(), 0.0); Assertions.assertEquals(1.0, receivePackets.getValue(), 0.0); } } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.meter.os; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import com.google.common.io.CharSource; import com.google.common.io.Files; import com.sun.management.OperatingSystemMXBean; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class TestSystemMeter { @Test public void testCalcMeasurement() throws IOException { MeterRegistry meterRegistry = new SimpleMeterRegistry(); CharSource charSource = Mockito.mock(CharSource.class); List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class); MockedStatic filesMockedStatic = Mockito.mockStatic(Files.class)) { fileUtilsMockedStatic.when(() -> { FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(list); filesMockedStatic.when(() -> { Files.asCharSource(Mockito.any(File.class), Mockito.any(Charset.class)); }).thenReturn(charSource); OsMeter osMeter = new OsMeter(meterRegistry); list.clear(); list.add("useless"); list.add("useless"); list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); SystemMeter systemMeter = osMeter.getCpuMeter(); OperatingSystemMXBean osBean = Mockito.mock(OperatingSystemMXBean.class); Mockito.when(osBean.getSystemLoadAverage()).thenReturn(0.775); Mockito.when(osBean.getCpuLoad()).thenReturn(0.875); Mockito.when(osBean.getProcessCpuLoad()).thenReturn(0.5); Mockito.when(osBean.getTotalMemorySize()).thenReturn(1000000000L); Mockito.when(osBean.getFreeMemorySize()).thenReturn(300000000L); systemMeter.setOsBean(osBean); NetMeter netMeter = osMeter.getNetMeter(); netMeter.setOsLinux(true); osMeter.poll(0, 1); MeasurementTree tree = new MeasurementTree(); tree.from(meterRegistry.getMeters().iterator(), new MeasurementGroupConfig("os", "type")); Assertions.assertEquals(0.875, tree.findChild("os", "cpu").getMeasurements().get(0).getValue(), 0.0); Assertions.assertEquals(0.5, tree.findChild("os", "processCpu").getMeasurements().get(0).getValue(), 0.0); Assertions.assertEquals(0.7, tree.findChild("os", "memory").getMeasurements().get(0).getValue(), 0.0); Assertions.assertEquals(0.775, tree.findChild("os", "sla").getMeasurements().get(0).getValue(), 0.0); Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(0).getValue(), 0.0); Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(1).getValue(), 0.0); Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(2).getValue(), 0.0); Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(3).getValue(), 0.0); } } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import java.util.Collections; import java.util.HashMap; import java.util.List; import org.apache.logging.log4j.core.LogEvent; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.PolledEvent; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; import org.junit.After; import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.runners.MethodSorters; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.Meter.Type; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Statistic; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.impl.VertxImpl; import jakarta.ws.rs.core.Response.Status; import mockit.Expectations; import mockit.Injectable; import mockit.Mock; import mockit.MockUp; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestDefaultLogPublisher { MeterRegistry meterRegistry = new SimpleMeterRegistry(); EventBus eventBus = new EventBus(); DefaultLogPublisher publisher = new DefaultLogPublisher(); LogCollector collector = new LogCollector(); Environment environment = Mockito.mock(Environment.class); @Before public void setup() { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); publisher.setEnvironment(environment); } @After public void tearDown() { collector.tearDown(); } @Test public void init_enabled_default() { Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); Holder registered = new Holder<>(false); new MockUp(eventBus) { @Mock void register(Object object) { registered.value = true; } }; publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); Assertions.assertFalse(registered.value); } @Test public void init_enabled_true() { Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(true); Holder registered = new Holder<>(); new MockUp(eventBus) { @Mock void register(Object object) { registered.value = true; } }; publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); Assertions.assertTrue(registered.value); } @Test public void init_enabled_false() { Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); Holder registered = new Holder<>(); new MockUp(eventBus) { @Mock void register(Object object) { registered.value = true; } }; publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); Assertions.assertNull(registered.value); } @Test public void onPolledEvent_failed() { publisher.onPolledEvent(null); LogEvent event = collector.getEvents().get(0); Assertions.assertEquals("Failed to print perf log.", event.getMessage().getFormattedMessage()); Assertions.assertEquals(NullPointerException.class, event.getThrown().getClass()); } @Test public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable MeasurementTree tree, @Injectable EventBus eventBus) { MetricsBootstrapConfig config = Mockito.mock(MetricsBootstrapConfig.class); try { Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(true); Mockito.when(config.getLatencyDistribution()).thenReturn("0,1,100"); publisher.init(meterRegistry, eventBus, config); new Expectations(VertxUtils.class) { { VertxUtils.getVertxMap(); result = Collections.singletonMap("v", vertxImpl); } }; DefaultPublishModel model = new DefaultPublishModel(); PerfInfo perfTotal = new PerfInfo(); perfTotal.setTotalRequests(10_0000); perfTotal.setMsTotalTime(30000L * 1_0000); perfTotal.setMsMaxLatency(30000); OperationPerf operationPerf = new OperationPerf(); operationPerf.setOperation("op"); operationPerf.setLatencyDistribution(new Integer[] {12, 120, 1200}); operationPerf.getStages().put(InvocationStageTrace.STAGE_TOTAL, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_QUEUE, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_PREPARE, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_WAIT, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_SEND, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_SEND, perfTotal); operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_CONNECTION, perfTotal); OperationPerfGroup operationPerfGroup = new OperationPerfGroup(CoreConst.RESTFUL, Status.OK.name()); operationPerfGroup.addOperationPerf(operationPerf); OperationPerfGroups operationPerfGroups = new OperationPerfGroups(); operationPerfGroups.getGroups().put(operationPerfGroup.getTransport(), Collections.singletonMap(operationPerfGroup.getStatus(), operationPerfGroup)); model.getConsumer().setOperationPerfGroups(operationPerfGroups); model.getProducer().setOperationPerfGroups(operationPerfGroups); model.getEdge().setOperationPerfGroups(operationPerfGroups); model.getThreadPools().put("test", new ThreadPoolPublishModel()); Id id = new Id("test", Tags.empty(), null, null, Type.OTHER); Measurement measurement = new Measurement(() -> 1.0, Statistic.VALUE); MeasurementNode measurementNodeCpuAll = new MeasurementNode("allProcess", id, new HashMap<>()); MeasurementNode measurementNodeCpuProcess = new MeasurementNode("currentProcess", id, new HashMap<>()); MeasurementNode measurementNodeSla = new MeasurementNode("sla", id, new HashMap<>()); MeasurementNode measurementNodeMemory = new MeasurementNode("memory", id, new HashMap<>()); MeasurementNode measurementNodeSend = new MeasurementNode("send", id, new HashMap<>()); MeasurementNode measurementNodeSendPacket = new MeasurementNode("sendPackets", id, new HashMap<>()); MeasurementNode measurementNodeRecv = new MeasurementNode("receive", id, new HashMap<>()); MeasurementNode measurementNodeRecvPacket = new MeasurementNode("receivePackets", id, new HashMap<>()); MeasurementNode measurementNodeEth0 = new MeasurementNode("eth0", id, new HashMap<>()); MeasurementNode measurementNodeNet = new MeasurementNode("net", id, new HashMap<>()); MeasurementNode measurementNodeOs = new MeasurementNode("os", id, new HashMap<>()); measurementNodeSend.getMeasurements().add(measurement); measurementNodeRecv.getMeasurements().add(measurement); measurementNodeCpuAll.getMeasurements().add(measurement); measurementNodeCpuProcess.getMeasurements().add(measurement); measurementNodeSla.getMeasurements().add(measurement); measurementNodeMemory.getMeasurements().add(measurement); measurementNodeRecvPacket.getMeasurements().add(measurement); measurementNodeSendPacket.getMeasurements().add(measurement); measurementNodeEth0.getChildren().put("send", measurementNodeSend); measurementNodeEth0.getChildren().put("receive", measurementNodeRecv); measurementNodeEth0.getChildren().put("receivePackets", measurementNodeRecvPacket); measurementNodeEth0.getChildren().put("sendPackets", measurementNodeSendPacket); measurementNodeNet.getChildren().put("eth0", measurementNodeEth0); measurementNodeOs.getChildren().put("cpu", measurementNodeCpuAll); measurementNodeOs.getChildren().put("processCpu", measurementNodeCpuProcess); measurementNodeOs.getChildren().put("sla", measurementNodeSla); measurementNodeOs.getChildren().put("memory", measurementNodeMemory); measurementNodeOs.getChildren().put("net", measurementNodeNet); measurementNodeOs.getMeasurements().add(measurement); measurementNodeNet.getMeasurements().add(measurement); measurementNodeEth0.getMeasurements().add(measurement); new MockUp() { @Mock DefaultPublishModel createDefaultPublishModel() { return model; } @Mock MeasurementTree getTree() { return tree; } }; new Expectations() { { tree.findChild(OsMeter.OS_NAME); result = measurementNodeOs; } }; publisher.onPolledEvent(new PolledEvent(Collections.emptyList())); List events = collector.getEvents().stream() .filter(e -> "scb-metrics".equals(e.getLoggerName())).toList(); LogEvent event = events.get(0); Assertions.assertEquals(""" os: cpu: all usage: 100.00% process usage: 100.00% sla: 1.00 memory usage: 100.00% net: send(Bps) recv(Bps) send(pps) recv(pps) interface 1 1 1 1 eth0 vertx: instances: name eventLoopContext-created v 0 threadPool: coreSize maxThreads poolSize currentBusy rejected queueSize taskCount taskFinished name 0 0 0 0 NaN 0 0.0 0.0 test consumer: simple: status requests latency [0,1) [1,100) [100,) operation rest.OK: \s 100000.0 3000.000/30000.000 12 120 1200 op 100000.0 3000.000/30000.000 12 120 1200 (summary) details: rest.OK: op: prepare : 3000.000/30000.000 connection : 3000.000/30000.000 encode-request: 3000.000/30000.000 send : 3000.000/30000.000 wait : 3000.000/30000.000 decode-response : 3000.000/30000.000 producer: simple: status requests latency [0,1) [1,100) [100,) operation rest.OK: \s 100000.0 3000.000/30000.000 12 120 1200 op 100000.0 3000.000/30000.000 12 120 1200 (summary) details: rest.OK: op: prepare: 3000.000/30000.000 decode-request : 3000.000/30000.000 queue : 3000.000/30000.000 business-execute: 3000.000/30000.000 encode-response: 3000.000/30000.000 send: 3000.000/30000.000 edge: simple: status requests latency [0,1) [1,100) [100,) operation rest.OK: \s 100000.0 3000.000/30000.000 12 120 1200 op 100000.0 3000.000/30000.000 12 120 1200 (summary) details: rest.OK: op: prepare : 3000.000/30000.000 provider-decode : 3000.000/30000.000 connection : 3000.000/30000.000 consumer-encode : 3000.000/30000.000 consumer-send : 3000.000/30000.000 wait : 3000.000/30000.000 consumer-decode : 3000.000/30000.000 provider-encode : 3000.000/30000.000 provider-send : 3000.000/30000.000 """.trim(), event.getMessage().getFormattedMessage().trim()); } catch (Exception e) { e.printStackTrace(); Assertions.fail("unexpected error happen. " + e.getMessage()); } } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.InvocationMetersInitializer; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.json.Json; import org.skyscreamer.jsonassert.JSONAssert; public class TestInvocationPublishModelFactory { EventBus eventBus = new EventBus(); // not step mode. MeterRegistry meterRegistry = new SimpleMeterRegistry(); InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer(); Invocation invocation = Mockito.mock(Invocation.class); InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); Response response = Mockito.mock(Response.class); InvocationType invocationType; Environment environment = Mockito.mock(Environment.class); @Test public void createDefaultPublishModel() throws Exception { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); Mockito.when(environment.getProperty(CONFIG_LATENCY_DISTRIBUTION, String.class)).thenReturn("0,1,100"); invocationMetersInitializer.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); prepareInvocation(); PublishModelFactory factory = new PublishModelFactory(meterRegistry.getMeters()); DefaultPublishModel model = factory.createDefaultPublishModel(); String expect = """ { "operationPerfGroups" : { "groups" : { "rest" : { "200" : { "transport" : "rest", "status" : "200", "operationPerfs" : [ { "operation" : "m.s.o", "stages" : { "consumer-encode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "prepare" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "wait" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "total" : { "totalRequests" : 1.0, "msTotalTime" : 1.4E-5, "msMaxLatency" : 1.4E-5 }, "consumer-send" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "connection" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "consumer-decode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 } }, "latencyDistribution" : [ 1, 1, 1 ] } ], "summary" : { "operation" : "", "stages" : { "consumer-encode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "prepare" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "total" : { "totalRequests" : 1.0, "msTotalTime" : 1.4E-5, "msMaxLatency" : 1.4E-5 }, "wait" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "consumer-send" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "connection" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "consumer-decode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 } }, "latencyDistribution" : [ 1, 1, 1 ] } } } } } } """; JSONAssert.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), Json.encodePrettily(model.getConsumer()), false); expect = """ { "operationPerfGroups" : { "groups" : { "rest" : { "200" : { "transport" : "rest", "status" : "200", "operationPerfs" : [ { "operation" : "m.s.o", "stages" : { "consumer-encode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "prepare" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "wait" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "total" : { "totalRequests" : 1.0, "msTotalTime" : 1.4E-5, "msMaxLatency" : 1.4E-5 }, "consumer-send" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "connection" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "consumer-decode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 } }, "latencyDistribution" : [ 1, 1, 1 ] } ], "summary" : { "operation" : "", "stages" : { "consumer-encode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "prepare" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "total" : { "totalRequests" : 1.0, "msTotalTime" : 1.4E-5, "msMaxLatency" : 1.4E-5 }, "wait" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "consumer-send" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "connection" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 }, "consumer-decode" : { "totalRequests" : 1.0, "msTotalTime" : 1.0000000000000002E-6, "msMaxLatency" : 1.0000000000000002E-6 } }, "latencyDistribution" : [ 1, 1, 1 ] } } } } } } """; JSONAssert.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), Json.encodePrettily(model.getProducer()), false); } protected void prepareInvocation() { Mockito.when(invocationStageTrace.calcTotal()).thenReturn(14L); Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(1L); Mockito.when(invocationStageTrace.calcConnection()).thenReturn(1L); Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(1L); Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(1L); Mockito.when(invocationStageTrace.calcWait()).thenReturn(1L); Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(1L); invocationType = InvocationType.CONSUMER; Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.isConsumer()).thenReturn(InvocationType.CONSUMER.equals(invocationType)); Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); Mockito.when(response.getStatusCode()).thenReturn(200); InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, response); eventBus.post(finishEvent); invocationType = InvocationType.PROVIDER; Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); eventBus.post(finishEvent); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import java.util.Map; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; import org.apache.servicecomb.metrics.core.publish.model.invocation.Utils; import org.junit.jupiter.api.Assertions; import jakarta.ws.rs.core.Response.Status; import org.junit.jupiter.api.Test; public class TestPublishUtils { String op = "op"; @Test public void createPerfInfo() { MeasurementNode stageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100); PerfInfo perf = PublishUtils.createPerfInfo(stageNode); Assertions.assertEquals(10, perf.getTotalRequests(), 0); Assertions.assertEquals(1000, perf.calcMsLatency(), 0); Assertions.assertEquals(100000, perf.getMsMaxLatency(), 0); } @Test public void createOperationPerf() { OperationPerf opPerf = Utils.createOperationPerf(op); PerfInfo perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_TOTAL); Integer[] latencyDistribution = opPerf.getLatencyDistribution(); Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); Assertions.assertEquals(2, latencyDistribution.length); Assertions.assertEquals(1, latencyDistribution[0].intValue()); Assertions.assertEquals(2, latencyDistribution[1].intValue()); } @Test public void addOperationPerfGroups() { OperationPerfGroups groups = new OperationPerfGroups(); PublishUtils.addOperationPerfGroups(groups, CoreConst.RESTFUL, op, Utils.createStatusNode(Status.OK.name(), Utils.totalStageNode)); Map statusMap = groups.getGroups().get(CoreConst.RESTFUL); OperationPerfGroup group = statusMap.get(Status.OK.name()); PerfInfo perfInfo = group.getSummary().findStage(InvocationStageTrace.STAGE_TOTAL); Integer[] latencyDistribution = group.getSummary().getLatencyDistribution(); Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); Assertions.assertEquals(2, latencyDistribution.length); Assertions.assertEquals(1, latencyDistribution[0].intValue()); Assertions.assertEquals(2, latencyDistribution[1].intValue()); } @Test public void createThreadPoolPublishModels_empty() { Map threadPools = ThreadPoolMonitorPublishModelFactory.create(new MeasurementTree()); Assertions.assertTrue(threadPools.isEmpty()); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.OperationConfig; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import com.google.common.eventbus.EventBus; @ExtendWith(MockitoExtension.class) public class TestSlowInvocationLogger { @Mock SCBEngine scbEngine; @Mock Invocation invocation; @Mock OperationMeta operationMeta; @Mock RestOperationMeta restOperationMeta; @Mock OperationConfig operationConfig; @Mock Response response; @Mock InvocationStageTrace stageTrace; InvocationFinishEvent event; SlowInvocationLogger logger; LogCollector logCollector; @BeforeEach public void setup() { EventBus eventBus = Mockito.spy(EventBus.class); Mockito.doNothing().when(eventBus).register(Mockito.any()); Mockito.when(scbEngine.getEventBus()).thenReturn(eventBus); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.getConfig()).thenReturn(operationConfig); logger = new SlowInvocationLogger(scbEngine); event = new InvocationFinishEvent(invocation, response); logCollector = new LogCollector(); } @AfterEach public void tearDown() { logCollector.tearDown(); } @Test public void disable() { logger.onInvocationFinish(event); Assertions.assertTrue(logCollector.getEvents().isEmpty()); } @Test public void enableButNotSlow() { Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(2L); Mockito.when(stageTrace.calcTotal()).thenReturn(1L); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); Assertions.assertTrue(logCollector.getEvents().isEmpty()); } @Test public void consumerSlow() { Endpoint endpoint = Mockito.mock(Endpoint.class); Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); Mockito.when(endpoint.getEndpoint()).thenReturn("rest://1.1.1.1:1234"); Mockito.when(invocation.isProducer()).thenReturn(false); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); Mockito.when(stageTrace.calcTotal()).thenReturn(1L); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); Assertions.assertEquals(""" Slow Consumer invocation [null](0 ms)[null] http method : null url : null endpoint : rest://1.1.1.1:1234 status code : 0 total : 0.0ms prepare : 0.0ms connection : 0.0ms consumer-encode : 0.0ms consumer-send : 0.0ms wait : 0.0ms consumer-decode : 0.0ms """, logCollector.getEvent(0).getMessage().getFormattedMessage()); } @Test public void edgeSlow() { Endpoint endpoint = Mockito.mock(Endpoint.class); Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); Mockito.when(endpoint.getEndpoint()).thenReturn("rest://1.1.1.1:1234"); Mockito.when(invocation.isEdge()).thenReturn(true); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); Mockito.when(stageTrace.calcTotal()).thenReturn(1L); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); Assertions.assertEquals(""" Slow Edge invocation [null](0 ms)[null] http method : null url : null endpoint : rest://1.1.1.1:1234 status code : 0 total : 0.0ms prepare : 0.0ms provider-decode : 0.0ms connection : 0.0ms consumer-encode : 0.0ms consumer-send : 0.0ms wait : 0.0ms consumer-decode : 0.0ms provider-encode : 0.0ms provider-send : 0.0ms """, logCollector.getEvent(0).getMessage().getFormattedMessage()); } @Test public void producerSlow() { HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class); Mockito.when(invocation.getRequestEx()).thenReturn(requestEx); Mockito.when(requestEx.getRemoteAddr()).thenReturn("1.1.1.1"); Mockito.when(requestEx.getRemotePort()).thenReturn(1234); Mockito.when(invocation.isProducer()).thenReturn(true); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); Mockito.when(stageTrace.calcTotal()).thenReturn(1L); Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); Assertions.assertEquals(""" Slow Provider invocation [null](0 ms)[null] http method : null url : null endpoint : 1.1.1.1:1234 status code : 0 total : 0.0ms prepare : 0.0ms provider-decode : 0.0ms queue : 0.0ms execute : 0.0ms provider-encode : 0.0ms provider-send : 0.0ms """, logCollector.getEvent(0).getMessage().getFormattedMessage()); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish; import java.util.concurrent.BlockingQueue; import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.ThreadPoolMetersInitializer; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.mockito.junit.jupiter.MockitoExtension; import org.skyscreamer.jsonassert.JSONAssert; @ExtendWith(MockitoExtension.class) @TestMethodOrder(MethodOrderer.MethodName.class) public class TestThreadPoolPublishModelFactory { MeterRegistry registry = new SimpleMeterRegistry(); @Mock BlockingQueue queue; @Test public void createDefaultPublishModel() throws Exception { ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class); Mockito.when(threadPoolExecutor.getQueue()).thenReturn(queue); Mockito.doReturn(10).when(queue).size(); MetricsBootstrapConfig metricsBootstrapConfig = Mockito.mock(MetricsBootstrapConfig.class); ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer() { @Override public void createThreadPoolMeters() { createThreadPoolMeters("test", threadPoolExecutor); } }; threadPoolMetersInitializer.init(registry, EventManager.getEventBus(), metricsBootstrapConfig); PublishModelFactory factory = new PublishModelFactory(registry.getMeters()); DefaultPublishModel model = factory.createDefaultPublishModel(); JSONAssert.assertEquals( """ {"test":{"avgTaskCount":0.0,"avgCompletedTaskCount":0.0,"currentThreadsBusy":0,"maxThreads":0,"poolSize":0,"corePoolSize":0,"queueSize":10,"rejected":0.0}}""", JsonUtils.writeValueAsString(model.getThreadPools()), false); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestOperationPerf { String op = "op"; OperationPerf opPerf = new OperationPerf(); @Test public void add() { Assertions.assertTrue(opPerf.getStages().isEmpty()); OperationPerf otherOpPerf = Utils.createOperationPerf(op); opPerf.add(otherOpPerf); Assertions.assertEquals(op, otherOpPerf.getOperation()); PerfInfo perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_TOTAL); Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; import jakarta.ws.rs.core.Response.Status; import org.junit.jupiter.api.Test; public class TestOperationPerfGroup { String op = "op"; OperationPerfGroup group = new OperationPerfGroup(CoreConst.RESTFUL, Status.OK.name()); @Test public void construct() { Assertions.assertEquals(CoreConst.RESTFUL, group.getTransport()); Assertions.assertEquals(Status.OK.name(), group.getStatus()); Assertions.assertTrue(group.getOperationPerfs().isEmpty()); Assertions.assertNull(group.getSummary()); } @Test public void addOperationPerf() { OperationPerf opPerf = Utils.createOperationPerf(op); group.addOperationPerf(opPerf); group.addOperationPerf(opPerf); Assertions.assertTrue(group.getOperationPerfs().contains(opPerf)); OperationPerf summary = group.getSummary(); PerfInfo perfInfo = summary.findStage(InvocationStageTrace.STAGE_TOTAL); Assertions.assertEquals(20, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); perfInfo = summary.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); Assertions.assertEquals(20, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestPerfInfo { @Test public void construct() { PerfInfo perf = new PerfInfo(); Assertions.assertEquals(0, perf.getTotalRequests(), 0); Assertions.assertEquals(0, perf.getMsTotalTime(), 0); Assertions.assertEquals(0, perf.getMsMaxLatency(), 0); Assertions.assertEquals(0, perf.calcMsLatency(), 0); } @Test public void add_changeMax() { PerfInfo sum = new PerfInfo(); PerfInfo other = new PerfInfo(); other.setTotalRequests(10); other.setMsTotalTime(10); other.setMsMaxLatency(100); sum.add(other); other = new PerfInfo(); other.setTotalRequests(20); other.setMsTotalTime(20); other.setMsMaxLatency(200); sum.add(other); Assertions.assertEquals(30, sum.getTotalRequests(), 0); Assertions.assertEquals(30, sum.getMsTotalTime(), 0); Assertions.assertEquals(200, sum.getMsMaxLatency(), 0); Assertions.assertEquals(1.0, sum.calcMsLatency(), 0); } @Test public void add_notChangeMax() { PerfInfo sum = new PerfInfo(); PerfInfo other = new PerfInfo(); other.setTotalRequests(10); other.setMsTotalTime(10); other.setMsMaxLatency(100); sum.add(other); other = new PerfInfo(); other.setTotalRequests(20); other.setMsTotalTime(20); other.setMsMaxLatency(50); sum.add(other); Assertions.assertEquals(30, sum.getTotalRequests(), 0); Assertions.assertEquals(1.0, sum.calcMsLatency(), 0); Assertions.assertEquals(100, sum.getMsMaxLatency(), 0); } @Test public void testToString() { PerfInfo perf = new PerfInfo(); perf.setTotalRequests(10); perf.setMsTotalTime(10); perf.setMsMaxLatency(100); Assertions.assertEquals("PerfInfo [tps=10.0, msTotalTime=10.0, msLatency=1.0, msMaxLatency=100.0]", perf.toString()); } } ================================================ FILE: metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.core.publish.model.invocation; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.publish.PublishUtils; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.Meter.Type; import io.micrometer.core.instrument.Statistic; import io.micrometer.core.instrument.Tags; import jakarta.ws.rs.core.Response.Status; public class Utils { public static MeasurementNode totalStageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100); public static MeasurementNode executeStageNode = Utils.createStageNode(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, 10, 10, 100); public static Id initId = new Id("id", Tags.empty(), null, null, Type.OTHER); public static MeasurementNode createStageNode(String stage, double count, double totalTime, double max) { Id id = initId; Measurement countMeasurement = new Measurement(() -> count, Statistic.COUNT); Measurement totalTimeMeasurement = new Measurement(() -> totalTime, Statistic.TOTAL_TIME); Measurement maxMeasurement = new Measurement(() -> max, Statistic.MAX); MeasurementNode stageNode = new MeasurementNode(stage, id, null); stageNode.addChild(Statistic.COUNT.name(), id, countMeasurement); stageNode.addChild(Statistic.TOTAL_TIME.name(), id, totalTimeMeasurement); stageNode.addChild(Statistic.MAX.name(), id, maxMeasurement); return stageNode; } public static MeasurementNode createStatusNode(String status, MeasurementNode... stageNodes) { Id id = initId; MeasurementNode statusNode = new MeasurementNode(status, id, new HashMap<>()); MeasurementNode typeNode = new MeasurementNode(MeterInvocationConst.TAG_STAGE, id, new HashMap<>()); MeasurementNode latencyNode = new MeasurementNode(MeterInvocationConst.TAG_DISTRIBUTION, id, new HashMap<>()); List measurements = latencyNode.getMeasurements(); measurements.add(new Measurement(() -> 1, Statistic.VALUE)); measurements.add(new Measurement(() -> 2, Statistic.VALUE)); for (MeasurementNode stageNode : stageNodes) { typeNode.getChildren().put(stageNode.getName(), stageNode); } statusNode.getChildren().put(latencyNode.getName(), latencyNode); statusNode.getChildren().put(typeNode.getName(), typeNode); return statusNode; } public static OperationPerf createOperationPerf(String op) { MeasurementNode statusNode = createStatusNode(Status.OK.name(), totalStageNode, executeStageNode); return PublishUtils.createOperationPerf(op, statusNode); } } ================================================ FILE: metrics/metrics-core/src/test/resources/log4j2.xml ================================================ ================================================ FILE: metrics/metrics-integration/metrics-prometheus/pom.xml ================================================ metrics-integration org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 metrics-prometheus Java Chassis::Metrics::Integration::Prometheus io.prometheus simpleclient io.prometheus simpleclient_httpserver org.apache.servicecomb metrics-core org.apache.servicecomb foundation-test-scaffolding ================================================ FILE: metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.prometheus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class PrometheusConfiguration { @Bean public PrometheusPublisher prometheusPublisher() { return new PrometheusPublisher(); } } ================================================ FILE: metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.prometheus; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.prometheus.client.Collector; import io.prometheus.client.Collector.MetricFamilySamples.Sample; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.HTTPServer; public class PrometheusPublisher extends Collector implements Collector.Describable, MetricsInitializer { private static final Logger LOGGER = LoggerFactory.getLogger(PrometheusPublisher.class); public static final String METRICS_PROMETHEUS_ADDRESS = "servicecomb.metrics.prometheus.address"; private HTTPServer httpServer; private MeterRegistry meterRegistry; private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { this.meterRegistry = meterRegistry; //prometheus default port allocation is here : https://github.com/prometheus/prometheus/wiki/Default-port-allocations String address = environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"); try { InetSocketAddress socketAddress = getSocketAddress(address); register(); this.httpServer = new HTTPServer(socketAddress, CollectorRegistry.defaultRegistry, true); LOGGER.info("Prometheus httpServer listened : {}.", address); } catch (Exception e) { throw new ServiceCombException("create http publish server failed,may bad address : " + address, e); } } private InetSocketAddress getSocketAddress(String address) { String[] hostAndPort = address.split(":"); if (hostAndPort.length == 2) { return new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1])); } throw new ServiceCombException("create http publish server failed,bad address : " + address); } @Override public List describe() { List familySamples = new ArrayList<>(); List samples = new ArrayList<>(); for (Meter meter : this.meterRegistry.getMeters()) { meter.measure().forEach(measurement -> { Sample sample = convertMeasurementToSample(meter, measurement); samples.add(sample); }); } familySamples.add(new MetricFamilySamples("ServiceComb_Metrics", Type.UNKNOWN, "ServiceComb Metrics", samples)); return familySamples; } protected Sample convertMeasurementToSample(Meter meter, Measurement measurement) { String prometheusName = meter.getId().getName().replace(".", "_"); List labelNames = new ArrayList<>(); List labelValues = new ArrayList<>(); labelNames.add("appId"); labelValues.add(BootStrapProperties.readApplication(environment)); for (Tag tag : meter.getId().getTags()) { labelNames.add(tag.getKey()); labelValues.add(tag.getValue()); } return new Sample(prometheusName, labelNames, labelValues, measurement.getValue()); } @Override public List collect() { return describe(); } @Override public void destroy() { if (httpServer == null) { return; } httpServer.close(); httpServer = null; LOGGER.info("Prometheus httpServer stopped."); } } ================================================ FILE: metrics/metrics-integration/metrics-prometheus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.metrics.prometheus.PrometheusConfiguration ================================================ FILE: metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.metrics.prometheus; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import static org.apache.servicecomb.metrics.prometheus.PrometheusPublisher.METRICS_PROMETHEUS_ADDRESS; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.sun.net.httpserver.HttpServer; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.prometheus.client.exporter.HTTPServer; public class TestPrometheusPublisher { MeterRegistry meterRegistry = new SimpleMeterRegistry(); PrometheusPublisher publisher = new PrometheusPublisher(); Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { publisher.setEnvironment(environment); Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); } @AfterAll public static void teardown() { } @Test public void testBadPublishAddress() { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("a:b:c"); Assertions.assertThrows(ServiceCombException.class, () -> { publisher.init(meterRegistry, null, null); }); } @Test public void testBadPublishAddress_BadPort() { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("localhost:xxxx"); Assertions.assertThrows(ServiceCombException.class, () -> { publisher.init(meterRegistry, null, null); }); } @Test public void testBadPublishAddress_TooLargePort() { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("localhost:9999999"); Assertions.assertThrows(ServiceCombException.class, () -> { publisher.init(meterRegistry, null, null); }); } @Test public void collect() throws IllegalAccessException, IOException { Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)).thenReturn("testAppId"); Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("localhost:0"); publisher.setEnvironment(environment); publisher.init(meterRegistry, null, new MetricsBootstrapConfig(environment)); Counter counter = meterRegistry.counter("count.name", "tag1", "tag1v", "tag2", "tag2v"); counter.increment(); HTTPServer httpServer = (HTTPServer) FieldUtils.readField(publisher, "httpServer", true); com.sun.net.httpserver.HttpServer server = (HttpServer) FieldUtils.readField(httpServer, "server", true); URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/metrics"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); try (InputStream is = conn.getInputStream()) { Assertions.assertEquals(""" # HELP ServiceComb_Metrics ServiceComb Metrics # TYPE ServiceComb_Metrics untyped count_name{appId="testAppId",tag1="tag1v",tag2="tag2v",} 1.0 """, IOUtils.toString(is, StandardCharsets.UTF_8)); } publisher.destroy(); } } ================================================ FILE: metrics/metrics-integration/pom.xml ================================================ metrics org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 metrics-integration Java Chassis::Metrics::Integration pom metrics-prometheus ================================================ FILE: metrics/pom.xml ================================================ 4.0.0 pom org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default metrics Java Chassis::Metrics metrics-core metrics-integration ================================================ FILE: parents/default/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-dependencies 3.4.0-SNAPSHOT ../../dependencies/default java-chassis-parent pom Java Chassis::Parent::Default Java Chassis Parent https://github.com/apache/servicecomb-java-chassis org.awaitility awaitility test org.hamcrest hamcrest-all test org.hamcrest hamcrest-core test org.mockito mockito-core test net.bytebuddy byte-buddy test com.github.seanyinx unit-scaffolding test junit junit test org.junit.jupiter junit-jupiter test org.junit.platform junit-platform-launcher test org.junit.vintage junit-vintage-engine test org.assertj assertj-core test org.springframework.boot spring-boot-starter-test org.springframework.boot spring-boot-starter-logging test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-api test org.apache.logging.log4j log4j-core test ================================================ FILE: parents/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis 3.4.0-SNAPSHOT java-chassis-parents Java Chassis::Parents pom default ================================================ FILE: pom.xml ================================================ 4.0.0 org.apache apache 35 org.apache.servicecomb java-chassis 3.4.0-SNAPSHOT pom ServiceComb Java Chassis Software Development Kit (SDK) for rapid development of microservices https://github.com/apache/servicecomb-java-chassis 17 17 UTF-8 17 true 3.6.0 4.3.0 12.2.0 0.48.0 3.6.3 3.2.8 0.8.14 3.12.0 2.5 3.15.0 3.5.4 3.4.2 3.9.0 3.2.0 1.7.1 0.6.1 3.19.2 1.47.0 12.3.1 3.1.1 3.21.0 3.3.0 4.9.8.2 3.5.4 3.5.4 Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo Development List dev-subscribe@servicecomb.apache.org dev-unsubscribe@servicecomb.apache.org dev@servicecomb.apache.org Commits List commits-subscribe@servicecomb.apache.org commits-unsubscribe@servicecomb.apache.org commits@servicecomb.apache.org The ServiceComb Developer Team dev@servicecomb.apache.org jira https://issues.apache.org/jira/browse/SCB The Apache Software Foundation http://www.apache.org/ 2017 https://github.com/apache/servicecomb-java-chassis scm:git:https://github.com/apache/servicecomb-java-chassis.git scm:git:https://github.com/apache/servicecomb-java-chassis.git v${project.version} apache.releases.https Apache Release Distribution Repository https://repository.apache.org/service/local/staging/deploy/maven2 apache.snapshots.https Apache Development Snapshot Repository https://repository.apache.org/content/repositories/snapshots dependencies parents common foundations handlers providers transports swagger core service-registry tracing edge metrics dynamic-config spring-boot solutions clients governance huawei-cloud kr.motd.maven os-maven-plugin ${os-maven-plugin.version} org.apache.maven.plugins maven-checkstyle-plugin ci/checkstyle/checkstyle.xml ci/checkstyle/suppressions.xml true org.apache.maven.plugins maven-remote-resources-plugin process-resource-bundles process ${skip-remote-resource} org.apache:apache-jar-resource-bundle:1.4 org.apache:apache-incubator-disclaimer-resource-bundle:1.1 Apache ServiceComb org.codehaus.mojo license-maven-plugin 2.7.1 default-cli true /org/codehaus/mojo/license/third-party-file-groupByMultiLicense.ftl The Apache Software License, Version 2.0|The Apache License, Version 2.0 The Apache Software License, Version 2.0|Apache License, Version 2.0 The Apache Software License, Version 2.0|Apache Public License 2.0 The Apache Software License, Version 2.0|Apache 2 The Apache Software License, Version 2.0|Apache 2.0 The Apache Software License, Version 2.0|Apache-2.0 The Apache Software License, Version 2.0|Apache License 2.0 The Apache Software License, Version 2.0|Apache License, version 2.0 3-Clause BSD License|BSD 3-clause 3-Clause BSD License|BSD 3-Clause Eclipse Public License v1.0|Eclipse Public License 1.0 Eclipse Public License v1.0|Eclipse Public License - v 1.0 The MIT License|MIT License org.apache.servicecomb* org.apache.rat apache-rat-plugin DISCLAIMER .travis.yml **/*.md **/*.MD **/*.iml .github/** **/etc/eclipse-java-google-style.xml **/etc/intellij-java-google-style.xml **/resources/ssl/** **/src/release/licenses/** **/logs/**.log **/**.log **/target/** **/resources/webroot/images/*.png **/resources/webroot/images/*.jpg **/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java **/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java **/java/org/apache/servicecomb/foundation/protobuf/internal/model/ProtobufRoot.java **/resources/google/protobuf/**.proto org.codehaus.mojo exec-maven-plugin ${exec-maven-plugin.version} java org.apache.maven.plugins maven-surefire-plugin ${surefire-maven-plugin.version} -Djdk.attach.allowAttachSelf --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.nio.channels.spi=ALL-UNNAMED --add-opens java.base/java.nio.file=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens java.base/java.util.jar=ALL-UNNAMED --add-opens java.base/java.util.stream=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/sun.net.dns=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/sun.security.jca=ALL-UNNAMED --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED --add-reads java.base=java.desktop ${maven.test.skip} ${maven.test.failure.ignore} ${excludesFile} 5 2 true alphabetical org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} true true com.github.spotbugs spotbugs-maven-plugin true true Medium ${session.executionRootDirectory}/ci/spotbugs/exclude.xml check org.eluder.coveralls coveralls-maven-plugin ${coveralls-maven-plugin.version} ${project.basedir}/coverage-reports/target/site/jacoco-aggregate/jacoco.xml maven-remote-resources-plugin ${maven-remote-resources-plugin.version} io.fabric8 docker-maven-plugin ${docker-maven-plugin.version} org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} *$Impl_* default-prepare-agent prepare-agent ${project.build.directory}/jacoco.exec default-prepare-agent-integration pre-integration-test prepare-agent-integration ${project.build.directory}/jacoco-it.exec jacoco.failsafe.argLine default-report report default-report-integration report-integration org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} -parameters true true -Xlint:all org.apache.maven.plugins maven-project-info-reports-plugin ${maven-project-info-reports-plugin.version} org.apache.maven.plugins maven-site-plugin ${site-maven-plugin.version} org.apache.maven.plugins maven-checkstyle-plugin ${checkstyle-maven-plugin.version} true com.puppycrawl.tools checkstyle ${puppycrawl-checkstyle.version} org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} repackage ${main.class} com.github.spotbugs spotbugs-maven-plugin ${spotbug-maven-plugin.version} release distribution false org.apache.maven.plugins maven-deploy-plugin org.apache.maven.plugins maven-gpg-plugin ${gpg-maven-plugin.version} sign-artifacts verify sign org.apache.maven.plugins maven-source-plugin ${source-maven-plugin.version} attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin ${javadoc-maven-plugin.version} attach-javadocs jar 17 created t Date created: org.apache.maven.plugins maven-release-plugin ${release-maven-plugin.version} true false release deploy it demo coverage coverage-reports docker-machine io.fabric8 docker-maven-plugin default true jacoco org.jacoco jacoco-maven-plugin owasp-dependency-check org.owasp dependency-check-maven ${dependency-check-maven-plugin.version} src/owasp-dependency-check-suppressions.xml 7 false false false false false false false false false false false false aggregate org.owasp dependency-check-maven ${dependency-check-maven-plugin.version} aggregate ================================================ FILE: providers/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default providers Java Chassis::Providers pom provider-jaxrs provider-pojo provider-rest-common provider-springmvc ================================================ FILE: providers/provider-jaxrs/pom.xml ================================================ 4.0.0 org.apache.servicecomb providers 3.4.0-SNAPSHOT provider-jaxrs Java Chassis::Providers::JAXRS org.apache.servicecomb provider-rest-common org.apache.servicecomb swagger-invocation-jaxrs ================================================ FILE: providers/provider-pojo/pom.xml ================================================ 4.0.0 org.apache.servicecomb providers 3.4.0-SNAPSHOT provider-pojo Java Chassis::Providers::POJO org.apache.servicecomb java-chassis-core org.apache.servicecomb foundation-test-scaffolding test org.apache.servicecomb registry-local test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.mockito mockito-inline test ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.springframework.util.ReflectionUtils; public class DefaultMethodMeta { private final Map defaultMethodHandles = new ConcurrentHashMapEx<>(); // java11 private final Method privateLookupIn = ReflectionUtils.findMethod(MethodHandles.class, "privateLookupIn", Class.class, Lookup.class); // only for java8 private Constructor lookupConstructor; public MethodHandle getOrCreateMethodHandle(Object proxy, Method method) { return defaultMethodHandles.computeIfAbsent(method, key -> createMethodHandle(proxy, method)); } protected MethodHandle createMethodHandle(Object proxy, Method method) { try { if (privateLookupIn != null) { return createForJava11(proxy, method); } return createForJava8(proxy, method); } catch (Exception e) { throw AsyncUtils.rethrow(e); } } protected MethodHandle createForJava11(Object proxy, Method method) throws Exception { Lookup lookup = MethodHandles.lookup(); Lookup privateLookup = (Lookup) privateLookupIn.invoke(null, method.getDeclaringClass(), lookup); return privateLookup .unreflectSpecial(method, method.getDeclaringClass()) .bindTo(proxy); } protected MethodHandle createForJava8(Object proxy, Method method) throws Exception { if (lookupConstructor == null) { lookupConstructor = ReflectionUtils.accessibleConstructor(Lookup.class, Class.class); } return lookupConstructor.newInstance(method.getDeclaringClass()) .unreflectSpecial(method, method.getDeclaringClass()) .bindTo(proxy); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/FilterInvocationCaller.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import static org.apache.servicecomb.core.provider.consumer.InvokerUtils.isAsyncMethod; import java.lang.reflect.Method; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; public class FilterInvocationCaller implements InvocationCaller { // if not a sync method, should never throw exception directly @Override public Object call(Method method, PojoConsumerMetaRefresher metaRefresher, PojoInvocationCreator invocationCreator, Object[] args) { if (isAsyncMethod(method)) { CompletableFuture invocationFuture = invocationCreator.createAsync(method, metaRefresher, args); return invocationFuture.thenCompose(this::doCall); } PojoInvocation invocation = invocationCreator.create(method, metaRefresher, args); CompletableFuture future = CompletableFuture.completedFuture(invocation) .thenCompose(this::doCall); return InvokerUtils.toSync(future, invocation.getWaitTime()); } protected CompletableFuture doCall(PojoInvocation invocation) { return InvokerUtils.invoke(invocation) .thenApply(response -> { if (response.isSucceed()) { return invocation.convertResponse(response); } throw ExceptionFactory.convertConsumerException(response.getResult()); }); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InstanceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; public interface InstanceFactory { String getImplName(); Object create(String implValue); } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InvocationCaller.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import java.lang.reflect.Method; public interface InvocationCaller { Object call(Method method, PojoConsumerMetaRefresher metaRefresher, PojoInvocationCreator invocationCreator, Object[] args); } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.servicecomb.core.SCBEngine; public class Invoker implements InvocationHandler { public static final Method METHOD_HASH_CODE; public static final Method METHOD_EQUALS; public static final Method METHOD_TO_STRING; static { try { METHOD_HASH_CODE = Object.class.getDeclaredMethod("hashCode"); METHOD_EQUALS = Object.class.getDeclaredMethod("equals", Object.class); METHOD_TO_STRING = Object.class.getDeclaredMethod("toString"); } catch (NoSuchMethodException e) { throw new Error(e); } } protected final PojoConsumerMetaRefresher metaRefresher; protected final PojoInvocationCreator invocationCreator; protected final DefaultMethodMeta defaultMethodMeta = new DefaultMethodMeta(); protected InvocationCaller invocationCaller; @SuppressWarnings("unchecked") public static T createProxy(String microserviceName, String schemaId, Class consumerIntf) { Invoker invoker = new Invoker(microserviceName, schemaId, consumerIntf); return (T) Proxy.newProxyInstance(consumerIntf.getClassLoader(), new Class[] {consumerIntf}, invoker); } public Invoker(String microserviceName, String schemaId, Class consumerIntf) { this.metaRefresher = createInvokerMeta(microserviceName, schemaId, consumerIntf); this.invocationCreator = createInvocationCreator(); } protected PojoConsumerMetaRefresher createInvokerMeta(String microserviceName, String schemaId, Class consumerIntf) { return new PojoConsumerMetaRefresher(microserviceName, schemaId, consumerIntf); } public PojoInvocationCreator createInvocationCreator() { return new PojoInvocationCreator(); } protected InvocationCaller createInvocationCaller() { return new FilterInvocationCaller(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.isDefault()) { return defaultMethodMeta.getOrCreateMethodHandle(proxy, method) .invokeWithArguments(args); } if (METHOD_HASH_CODE.equals(method)) { return objectHashCode(proxy); } if (METHOD_EQUALS.equals(method)) { return objectEquals(proxy, args[0]); } if (METHOD_TO_STRING.equals(method)) { return objectToString(proxy); } SCBEngine.getInstance().ensureStatusUp(); prepareInvocationCaller(); return invocationCaller.call(method, metaRefresher, invocationCreator, args); } protected void prepareInvocationCaller() { if (invocationCaller != null) { return; } this.invocationCaller = createInvocationCaller(); } private String objectClassName(Object obj) { return obj.getClass().getName(); } private int objectHashCode(Object obj) { return System.identityHashCode(obj); } private boolean objectEquals(Object obj, Object other) { return obj == other; } private String objectToString(Object obj) { return objectClassName(obj) + '@' + Integer.toHexString(objectHashCode(obj)); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; public final class PojoConst { private PojoConst() { } public static final String FIELD_SCHEMA_ID = "schemaId"; public static final String SCHEMA_ID = "schema-id"; public static final String FIELD_SCHEMA_INTERFACE = "schemaInterfaceName"; public static final String SCHEMA_INTERFACE = "schema-interface"; public static final String IMPL = "implementation"; public static final String FIELD_MICROSERVICE_NAME = "microserviceName"; public static final String MICROSERVICE_NAME = "microservice-name"; public static final String FIELD_INTERFACE = "consumerIntf"; public static final String INTERFACE = "interface"; public static final String POJO = "pojo"; public static final String SPRING = "spring"; } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerMetaRefresher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; import org.apache.servicecomb.provider.pojo.definition.PojoConsumerMeta; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jakarta.ws.rs.core.Response.Status; public class PojoConsumerMetaRefresher { private static final Logger LOGGER = LoggerFactory.getLogger(PojoConsumerMetaRefresher.class); protected final String microserviceName; // can be null, should find SchemaMeta by consumerIntf in this time protected final String schemaId; protected final Class consumerIntf; protected SCBEngine scbEngine; // not always equals codec meta // for highway, codec meta is relate to target instance // to avoid limit producer to only allow append parameter protected PojoConsumerMeta consumerMeta; public PojoConsumerMetaRefresher(String microserviceName, String schemaId, Class consumerIntf) { this.microserviceName = microserviceName; this.schemaId = schemaId; this.consumerIntf = consumerIntf; } public PojoConsumerMeta getLatestMeta() { ensureStatusUp(); ensureMetaAvailable(); return consumerMeta; } public CompletableFuture getLatestMetaAsync() { ensureStatusUp(); return ensureMetaAvailableAsync().thenCompose(v -> CompletableFuture.completedFuture(consumerMeta)); } private void ensureStatusUp() { if (scbEngine == null) { if (SCBEngine.getInstance() == null) { String message = "The request is rejected. Cannot process the request due to SCBEngine not ready."; LOGGER.warn(message); throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData(message)); } this.scbEngine = SCBEngine.getInstance(); } scbEngine.ensureStatusUp(); } private void ensureMetaAvailable() { if (isNeedRefresh()) { synchronized (this) { if (isNeedRefresh()) { this.consumerMeta = refreshMeta(); } } } } private CompletableFuture ensureMetaAvailableAsync() { if (isNeedRefresh()) { return refreshMetaAsync(); } return CompletableFuture.completedFuture(null); } private boolean isNeedRefresh() { return consumerMeta == null; } protected PojoConsumerMeta refreshMeta() { MicroserviceReferenceConfig microserviceReferenceConfig = scbEngine .getOrCreateReferenceConfig(microserviceName); if (microserviceReferenceConfig == null) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData(String.format("Failed to invoke service %s. Maybe service" + " not registered or no active instance.", microserviceName))); } MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getMicroserviceMeta(); SchemaMeta schemaMeta = findSchemaMeta(microserviceMeta); if (schemaMeta == null) { throw new IllegalStateException( String.format( "Schema not exist, microserviceName=%s, schemaId=%s, consumer interface=%s; " + "new producer not running or not deployed.", microserviceName, schemaId, consumerIntf.getName())); } SwaggerConsumer swaggerConsumer = scbEngine.getSwaggerEnvironment() .createConsumer(consumerIntf, schemaMeta.getSwagger()); return new PojoConsumerMeta(microserviceReferenceConfig, swaggerConsumer, schemaMeta); } protected CompletableFuture refreshMetaAsync() { CompletableFuture microserviceReferenceConfigFuture = scbEngine .getOrCreateReferenceConfigAsync(microserviceName); return microserviceReferenceConfigFuture.thenCompose((microserviceReferenceConfig) -> { if (microserviceReferenceConfig == null) { return CompletableFuture.failedFuture(new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData(String.format("Failed to invoke service %s. Maybe service" + " not registered or no active instance.", microserviceName)))); } synchronized (this) { if (isNeedRefresh()) { MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getMicroserviceMeta(); SchemaMeta schemaMeta = findSchemaMeta(microserviceMeta); if (schemaMeta == null) { return CompletableFuture.failedFuture(new IllegalStateException( String.format( "Schema not exist, microserviceName=%s, schemaId=%s, consumer interface=%s; " + "new producer not running or not deployed.", microserviceName, schemaId, consumerIntf.getName()))); } SwaggerConsumer swaggerConsumer = scbEngine.getSwaggerEnvironment() .createConsumer(consumerIntf, schemaMeta.getSwagger()); consumerMeta = new PojoConsumerMeta(microserviceReferenceConfig, swaggerConsumer, schemaMeta); } return CompletableFuture.completedFuture(null); } }); } private SchemaMeta findSchemaMeta(MicroserviceMeta microserviceMeta) { // if present schemaId, just use it if (StringUtils.isNotEmpty(schemaId)) { return microserviceMeta.findSchemaMeta(schemaId); } // try interface name second return microserviceMeta.findSchemaMeta(consumerIntf.getName()); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.provider.pojo.definition.PojoConsumerOperationMeta; import org.apache.servicecomb.swagger.invocation.Response; public class PojoInvocation extends Invocation { protected final PojoConsumerOperationMeta consumerOperationMeta; public PojoInvocation(PojoConsumerOperationMeta consumerOperationMeta) { super(consumerOperationMeta.createReferenceConfig(), consumerOperationMeta.getOperationMeta(), consumerOperationMeta.getInvocationRuntimeType(), null); this.consumerOperationMeta = consumerOperationMeta; InvocationFactory.setSrcMicroservice(this); } public ReferenceConfig getReferenceConfig() { return referenceConfig; } public Object convertResponse(Response response) { return consumerOperationMeta.getSwaggerConsumerOperation().getResponseMapper().mapResponse(response); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import java.lang.reflect.Method; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.provider.pojo.definition.PojoConsumerMeta; import org.apache.servicecomb.provider.pojo.definition.PojoConsumerOperationMeta; public class PojoInvocationCreator { public PojoInvocation create(Method method, PojoConsumerMetaRefresher metaRefresher, Object[] args) { long startCreateInvocation = System.nanoTime(); PojoConsumerMeta pojoConsumerMeta = metaRefresher.getLatestMeta(); PojoConsumerOperationMeta consumerOperationMeta = pojoConsumerMeta.ensureFindOperationMeta(method); PojoInvocation invocation = new PojoInvocation(consumerOperationMeta); invocation.setSuccessResponseType(consumerOperationMeta.getResponsesType()); invocation.setInvocationArguments(consumerOperationMeta.getSwaggerConsumerOperation().toInvocationArguments(args)); invocation.setSync(consumerOperationMeta.isSync()); invocation.getInvocationStageTrace().startCreateInvocation(startCreateInvocation); invocation.getInvocationStageTrace().finishCreateInvocation(); return invocation; } public CompletableFuture createAsync(Method method, PojoConsumerMetaRefresher metaRefresher, Object[] args) { CompletableFuture pojoConsumerMetaFuture = metaRefresher.getLatestMetaAsync(); return pojoConsumerMetaFuture.thenCompose(pojoConsumerMeta -> { PojoConsumerOperationMeta consumerOperationMeta = pojoConsumerMeta.ensureFindOperationMeta(method); PojoInvocation invocation = new PojoInvocation(consumerOperationMeta); invocation.setSuccessResponseType(consumerOperationMeta.getResponsesType()); invocation.setInvocationArguments( consumerOperationMeta.getSwaggerConsumerOperation().toInvocationArguments(args)); invocation.setSync(consumerOperationMeta.isSync()); return CompletableFuture.completedFuture(invocation); }); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoProducerProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.provider.producer.AbstractProducerProvider; import org.apache.servicecomb.core.provider.producer.ProducerMeta; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.pojo.instance.PojoInstanceFactory; import org.apache.servicecomb.provider.pojo.instance.SpringInstanceFactory; import org.apache.servicecomb.provider.pojo.schema.PojoProducerMeta; import org.apache.servicecomb.provider.pojo.schema.PojoProducers; public class PojoProducerProvider extends AbstractProducerProvider { private final Map instanceFactoryMgr = new HashMap<>(); private void registerInstanceFactory(InstanceFactory instanceFactory) { instanceFactoryMgr.put(instanceFactory.getImplName(), instanceFactory); } public PojoProducerProvider() { registerInstanceFactory(new PojoInstanceFactory()); registerInstanceFactory(new SpringInstanceFactory()); } @Override public List init() { // for some test cases, there is no spring context if (BeanUtils.getContext() == null) { return Collections.emptyList(); } PojoProducers pojoProducers = BeanUtils.getContext().getBean(PojoProducers.class); for (ProducerMeta producerMeta : pojoProducers.getProducerMetas()) { PojoProducerMeta pojoProducerMeta = (PojoProducerMeta) producerMeta; initPojoProducerMeta(pojoProducerMeta); } return pojoProducers.getProducerMetas(); } @Override public String getName() { return PojoConst.POJO; } private void initPojoProducerMeta(PojoProducerMeta pojoProducerMeta) { parseSchemaInterface(pojoProducerMeta); parseImplementation(pojoProducerMeta); } private void parseImplementation(PojoProducerMeta pojoProducerMeta) { if (pojoProducerMeta.getInstance() != null) { return; } String implementation = pojoProducerMeta.getImplementation(); String implName = PojoConst.POJO; String implValue = pojoProducerMeta.getImplementation(); int idx = implementation.indexOf(':'); if (idx != -1) { implName = implementation.substring(0, idx); implValue = implementation.substring(idx + 1); } InstanceFactory factory = instanceFactoryMgr.get(implName); if (factory == null) { throw new IllegalStateException("failed to find instance factory, name=" + implName); } Object instance = factory.create(implValue); pojoProducerMeta.setInstance(instance); } private void parseSchemaInterface(PojoProducerMeta pojoProducerMeta) { if (pojoProducerMeta.getSchemaInterface() != null || StringUtils .isEmpty(pojoProducerMeta.getSchemaInterfaceName())) { return; } try { Class si = Class.forName(pojoProducerMeta.getSchemaInterfaceName()); pojoProducerMeta.setSchemaInterface(si); } catch (Exception e) { throw new Error("can not find schema interface " + pojoProducerMeta.getSchemaInterfaceName(), e); } } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import org.apache.servicecomb.provider.pojo.schema.PojoProducers; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ProviderPojoConfiguration { @Bean public static RpcReferenceBeanDefinitionRegistry rpcReferenceBeanDefinitionRegistry() { return new RpcReferenceBeanDefinitionRegistry(); } @Bean public static PojoProducers pojoProducers() { return new PojoProducers(); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Documented @Retention(RUNTIME) @Target({FIELD, METHOD, ANNOTATION_TYPE}) public @interface RpcReference { String microserviceName(); String schemaId() default ""; } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import org.apache.servicecomb.provider.pojo.reference.RpcReferenceProcessor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; /** * In order to support FactoryBean eager inject @RpcReference, add RpcReferenceProcessor * before FactoryBean is initialized. */ public class RpcReferenceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.addBeanPostProcessor(new RpcReferenceProcessor(beanFactory)); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import org.springframework.stereotype.Component; @Inherited @Documented @Retention(RUNTIME) @Target({TYPE, ANNOTATION_TYPE}) @Component public @interface RpcSchema { String schemaId() default ""; Class schemaInterface() default Object.class; } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.definition; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import jakarta.ws.rs.core.Response.Status; public class PojoConsumerMeta { private final MicroserviceReferenceConfig microserviceReferenceConfig; private final SchemaMeta schemaMeta; private final Map operationMetas = new HashMap<>(); public PojoConsumerMeta(MicroserviceReferenceConfig microserviceReferenceConfig, SwaggerConsumer swaggerConsumer, SchemaMeta schemaMeta) { this.microserviceReferenceConfig = microserviceReferenceConfig; this.schemaMeta = schemaMeta; for (SwaggerConsumerOperation swaggerConsumerOperation : swaggerConsumer.getOperations().values()) { String operationId = swaggerConsumerOperation.getSwaggerOperation().getOperationId(); // SwaggerConsumer has make sure can find operationMeta OperationMeta operationMeta = schemaMeta.ensureFindOperation(operationId); PojoConsumerOperationMeta pojoConsumerOperationMeta = new PojoConsumerOperationMeta(this, operationMeta, swaggerConsumerOperation); operationMetas.put(swaggerConsumerOperation.getConsumerMethod(), pojoConsumerOperationMeta); } } public MicroserviceReferenceConfig getMicroserviceReferenceConfig() { return microserviceReferenceConfig; } public MicroserviceMeta getMicroserviceMeta() { return schemaMeta.getMicroserviceMeta(); } public SchemaMeta getSchemaMeta() { return schemaMeta; } public PojoConsumerOperationMeta ensureFindOperationMeta(Method method) { PojoConsumerOperationMeta pojoConsumerOperationMeta = operationMetas.get(method); if (pojoConsumerOperationMeta == null) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, String.format( "Consumer method %s:%s not exist in contract, microserviceName=%s, schemaId=%s.", method.getDeclaringClass().getName(), method.getName(), schemaMeta.getMicroserviceName(), schemaMeta.getSchemaId())); } return pojoConsumerOperationMeta; } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerOperationMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.definition; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findResponseTypeProcessor; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import com.google.common.reflect.TypeToken; public class PojoConsumerOperationMeta { private final PojoConsumerMeta pojoConsumerMeta; private final OperationMeta operationMeta; private final SwaggerConsumerOperation swaggerConsumerOperation; private JavaType responseType; private InvocationRuntimeType invocationRuntimeType; private final boolean sync; public PojoConsumerOperationMeta(PojoConsumerMeta pojoConsumerMeta, OperationMeta operationMeta, SwaggerConsumerOperation swaggerConsumerOperation) { this.pojoConsumerMeta = pojoConsumerMeta; this.operationMeta = operationMeta; this.swaggerConsumerOperation = swaggerConsumerOperation; this.sync = InvokerUtils.isSyncMethod(swaggerConsumerOperation.getConsumerMethod()); initResponseType(); initRuntimeType(); } private void initRuntimeType() { invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); invocationRuntimeType.setArgumentsMapper(swaggerConsumerOperation.getArgumentsMapper()); invocationRuntimeType.setAssociatedClass(swaggerConsumerOperation.getConsumerClass()); invocationRuntimeType.setAssociatedMethod(swaggerConsumerOperation.getConsumerMethod()); } public PojoConsumerMeta getPojoConsumerMeta() { return pojoConsumerMeta; } public OperationMeta getOperationMeta() { return operationMeta; } public SwaggerConsumerOperation getSwaggerConsumerOperation() { return swaggerConsumerOperation; } public JavaType getResponsesType() { return responseType; } public InvocationRuntimeType getInvocationRuntimeType() { return invocationRuntimeType; } public ReferenceConfig createReferenceConfig() { return pojoConsumerMeta.getMicroserviceReferenceConfig() .createReferenceConfig(operationMeta); } private void initResponseType() { Type intfResponseType = TypeToken.of(swaggerConsumerOperation.getConsumerClass()) .resolveType(swaggerConsumerOperation.getConsumerMethod().getGenericReturnType()) .getType(); if (intfResponseType instanceof Class && Part.class.isAssignableFrom((Class) intfResponseType)) { responseType = TypeFactory.defaultInstance().constructType(Part.class); return; } intfResponseType = findResponseTypeProcessor(intfResponseType).extractResponseType(intfResponseType); if (intfResponseType != null) { responseType = TypeFactory.defaultInstance().constructType(intfResponseType); } } public boolean isSync() { return sync; } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/PojoInstanceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.instance; import org.apache.servicecomb.provider.pojo.InstanceFactory; import org.apache.servicecomb.provider.pojo.PojoConst; public class PojoInstanceFactory implements InstanceFactory { @Override public String getImplName() { return PojoConst.POJO; } @Override public Object create(String className) { try { Class clazz = Class.forName(className); return clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new Error("Fail to create instance of class:" + className, e); } } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/SpringInstanceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.instance; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.pojo.InstanceFactory; import org.apache.servicecomb.provider.pojo.PojoConst; public class SpringInstanceFactory implements InstanceFactory { @Override public String getImplName() { return PojoConst.SPRING; } @Override public Object create(String beanId) { Object instance = BeanUtils.getBean(beanId); if (instance == null) { throw new Error("Fail to find bean:" + beanId); } return instance; } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/PojoReferenceMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.reference; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.provider.pojo.Invoker; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; public class PojoReferenceMeta implements FactoryBean, InitializingBean { // 原始数据 private String microserviceName; private String schemaId; private Class consumerIntf; // 根据intf创建出来的动态代理 // TODO:未实现本地优先(本地场景下,应该跳过handler机制) private Object proxy; public Object getProxy() { return getObject(); } @Override public Object getObject() { return proxy; } @Override public Class getObjectType() { return consumerIntf; } @Override public boolean isSingleton() { return true; } public void setConsumerIntf(Class intf) { this.consumerIntf = intf; } public void setMicroserviceName(String microserviceName) { this.microserviceName = microserviceName; } public void setSchemaId(String schemaId) { this.schemaId = schemaId; } @Override public void afterPropertiesSet() { if (consumerIntf == null) { throw new ServiceCombException( String.format( "microserviceName=%s, schemaid=%s, \n" + "do not support implicit interface anymore, \n" + "because that caused problems:\n" + " 1.the startup process relies on other microservices\n" + " 2.cyclic dependent microservices can not be deployed\n" + "suggest to use @RpcReference or " + ".", microserviceName, schemaId)); } proxy = Invoker.createProxy(microserviceName, schemaId, consumerIntf); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/ReferenceDefParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.reference; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.provider.pojo.PojoConst; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; public class ReferenceDefParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { return PojoReferenceMeta.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { builder.addPropertyValue(PojoConst.FIELD_MICROSERVICE_NAME, element.getAttribute(PojoConst.MICROSERVICE_NAME)); String schemaId = element.getAttribute(PojoConst.SCHEMA_ID); String intf = element.getAttribute(PojoConst.INTERFACE); if (StringUtils.isEmpty(intf) && StringUtils.isNotEmpty(schemaId)) { // 尝试将schemaId当作接口名使用 Class consumerIntf = ReflectUtils.getClassByName(schemaId); if (consumerIntf != null) { intf = schemaId; } } builder.addPropertyValue(PojoConst.FIELD_SCHEMA_ID, schemaId); builder.addPropertyValue(PojoConst.FIELD_INTERFACE, intf); if (StringUtils.isEmpty(schemaId) && StringUtils.isEmpty(intf)) { throw new Error("schema-id and interface can not both be empty."); } } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.reference; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.servicecomb.provider.pojo.RpcReference; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.util.ReflectionUtils; public class RpcReferenceProcessor implements BeanPostProcessor { private final ConfigurableBeanFactory beanFactory; public RpcReferenceProcessor(ConfigurableBeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 扫描所有field,处理扩展的field标注 ReflectionUtils.doWithFields(bean.getClass(), field -> processConsumerField(bean, field)); ReflectionUtils.doWithMethods(bean.getClass(), method -> processConsumerMethod(bean, beanName, method)); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } protected void processConsumerMethod(Object bean, String beanName, Method method) throws BeansException { RpcReference reference = method.getAnnotation(RpcReference.class); if (reference == null) { return; } handleReferenceMethod(bean, beanName, method, reference); } protected void processConsumerField(Object bean, Field field) { RpcReference reference = field.getAnnotation(RpcReference.class); if (reference == null) { return; } handleReferenceField(bean, field, reference); } private void handleReferenceMethod(Object bean, String beanName, Method method, RpcReference reference) throws BeansException { try { PojoReferenceMeta pojoReference = createPojoReferenceMeta(reference, method.getParameterTypes()[0]); method.invoke(bean, pojoReference.getProxy()); } catch (Exception e) { throw new BeanCreationException(beanName, "", e); } } private void handleReferenceField(Object obj, Field field, RpcReference reference) { PojoReferenceMeta pojoReference = createPojoReferenceMeta(reference, field.getType()); ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, obj, pojoReference.getProxy()); } private PojoReferenceMeta createPojoReferenceMeta(RpcReference reference, Class consumerInterface) { String microserviceName = reference.microserviceName(); microserviceName = beanFactory.resolveEmbeddedValue(microserviceName); PojoReferenceMeta pojoReference = new PojoReferenceMeta(); pojoReference.setMicroserviceName(microserviceName); pojoReference.setSchemaId(reference.schemaId()); pojoReference.setConsumerIntf(consumerInterface); pojoReference.afterPropertiesSet(); return pojoReference; } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.schema; import org.apache.servicecomb.core.provider.producer.ProducerMeta; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; public class PojoProducerMeta extends ProducerMeta implements InitializingBean { @Autowired protected PojoProducers pojoProducers; private String implementation; private String schemaInterfaceName; @VisibleForTesting void setPojoProducers(PojoProducers pojoProducers) { this.pojoProducers = pojoProducers; } public String getImplementation() { return implementation; } public void setImplementation(String implementation) { this.implementation = implementation; } public String getSchemaInterfaceName() { return schemaInterfaceName; } public PojoProducerMeta setSchemaInterfaceName(String schemaInterfaceName) { this.schemaInterfaceName = schemaInterfaceName; return this; } @Override public void afterPropertiesSet() { pojoProducers.registerPojoProducer(this); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.schema; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.provider.producer.ProducerMeta; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class PojoProducers implements BeanPostProcessor { private final List producerMetas = new ArrayList<>(); public synchronized void registerPojoProducer(PojoProducerMeta pojoProducer) { producerMetas.add(pojoProducer); } public List getProducerMetas() { return producerMetas; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { processProvider(beanName, bean); return bean; } protected void processProvider(String beanName, Object bean) { // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class // 所以,需要先取出原class,再取标注 Class beanCls = BeanUtils.getImplClassFromBean(bean); if (beanCls == null) { return; } RpcSchema rpcSchema = beanCls.getAnnotation(RpcSchema.class); if (rpcSchema == null) { return; } String schemaId = rpcSchema.schemaId(); if (StringUtils.isEmpty(schemaId)) { Class[] intfs = beanCls.getInterfaces(); if (intfs.length == 1) { schemaId = intfs[0].getName(); } else { throw new Error("Must be schemaId or implements only one interface"); } } PojoProducerMeta pojoProducerMeta = new PojoProducerMeta(); pojoProducerMeta.setSchemaId(schemaId); pojoProducerMeta.setSchemaInterface(rpcSchema.schemaInterface()); pojoProducerMeta.setInstance(bean); registerPojoProducer(pojoProducerMeta); } } ================================================ FILE: providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/SchemaDefParser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.schema; import org.apache.servicecomb.provider.pojo.PojoConst; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; public class SchemaDefParser extends AbstractSingleBeanDefinitionParser { @Override protected boolean shouldGenerateId() { return true; } @Override protected boolean shouldParseNameAsAliases() { return false; } @Override protected Class getBeanClass(Element element) { return PojoProducerMeta.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { builder.addPropertyValue(PojoConst.FIELD_SCHEMA_ID, element.getAttribute(PojoConst.SCHEMA_ID)); builder.addPropertyValue(PojoConst.IMPL, element.getAttribute(PojoConst.IMPL)); builder.addPropertyValue(PojoConst.FIELD_SCHEMA_INTERFACE, element.getAttribute(PojoConst.SCHEMA_INTERFACE)); } } ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.provider.pojo.PojoProducerProvider ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/spring/namespace.properties ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # rpc-reference=org.apache.servicecomb.provider.pojo.reference.ReferenceDefParser rpc-schema=org.apache.servicecomb.provider.pojo.schema.SchemaDefParser ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.provider.pojo.ProviderPojoConfiguration ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc-1.0.xsd ================================================ ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc.xsd ================================================ ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/spring.handlers ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- http\://www.huawei.com/schema/paas/cse/rpc=org.apache.servicecomb.foundation.common.spring.PaasNamespaceHandler ================================================ FILE: providers/provider-pojo/src/main/resources/META-INF/spring.schemas ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- http\://www.huawei.com/schema/paas/cse/rpc/spring-paas-cse-rpc-1.0.xsd=META-INF/spring/spring-paas-cse-rpc-1.0.xsd http\://www.huawei.com/schema/paas/cse/rpc/spring-paas-cse-rpc.xsd=META-INF/spring/spring-paas-cse-rpc-1.0.xsd ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/IPerson.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; public interface IPerson { } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; @RpcSchema(schemaId = "test") public class Person implements IPerson { public String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/PersonReference.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo; public class PersonReference { @RpcReference(microserviceName = "test", schemaId = "test") public IPerson person; } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/instance/TestPojoInstanceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.instance; import org.apache.servicecomb.provider.pojo.PojoConst; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestPojoInstanceFactory { @Test public void testInitException() { PojoInstanceFactory lPojoInstanceFactory = new PojoInstanceFactory(); try { lPojoInstanceFactory.create("TestPojoInstanceFactory"); } catch (Error e) { Assertions.assertEquals("Fail to create instance of class:TestPojoInstanceFactory", e.getMessage()); } } @Test public void testInit() { PojoInstanceFactory lPojoInstanceFactory = new PojoInstanceFactory(); lPojoInstanceFactory.create("org.apache.servicecomb.provider.pojo.instance.TestPojoInstanceFactory"); Assertions.assertEquals(PojoConst.POJO, lPojoInstanceFactory.getImplName()); } } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/instance/TestSpringInstanceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.instance; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.provider.pojo.PojoConst; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; public class TestSpringInstanceFactory { @Test public void testInitException() { SpringInstanceFactory lSpringInstanceFactory = new SpringInstanceFactory(); try (MockedStatic beanUtilsMockedStatic = Mockito.mockStatic(BeanUtils.class)) { beanUtilsMockedStatic.when(BeanUtils::getContext).thenReturn(Mockito.mock(ApplicationContext.class)); beanUtilsMockedStatic.when(() -> BeanUtils.getBean(Mockito.anyString())).thenReturn(null); try { lSpringInstanceFactory.create("TestSpringInstanceFactory"); } catch (Error e) { Assertions.assertEquals("Fail to find bean:TestSpringInstanceFactory", e.getMessage()); } } } @Test public void testInit() { SpringInstanceFactory lSpringInstanceFactory = new SpringInstanceFactory(); try (MockedStatic beanUtilsMockedStatic = Mockito.mockStatic(BeanUtils.class)) { beanUtilsMockedStatic.when(BeanUtils::getContext).thenReturn(Mockito.mock(ApplicationContext.class)); beanUtilsMockedStatic.when(() -> BeanUtils.getBean(Mockito.anyString())).thenReturn(new Object()); lSpringInstanceFactory.create("org.apache.servicecomb.provider.pojo.instance.TestPojoInstanceFactory"); Assertions.assertEquals(PojoConst.SPRING, lSpringInstanceFactory.getImplName()); } } } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/reference/PojoReferenceMetaTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.reference; import static org.hamcrest.core.IsInstanceOf.instanceOf; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.provider.pojo.IPerson; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class PojoReferenceMetaTest { static Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { } @AfterEach public void teardown() { } @Test public void testHasConsumerInterface() { SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest(environment); PojoReferenceMeta pojoReferenceMeta = new PojoReferenceMeta(); pojoReferenceMeta.setMicroserviceName("test"); pojoReferenceMeta.setSchemaId("schemaId"); pojoReferenceMeta.setConsumerIntf(IPerson.class); pojoReferenceMeta.afterPropertiesSet(); Assertions.assertEquals(IPerson.class, pojoReferenceMeta.getObjectType()); MatcherAssert.assertThat(pojoReferenceMeta.getProxy(), instanceOf(IPerson.class)); Assertions.assertTrue(pojoReferenceMeta.isSingleton()); scbEngine.destroy(); } @Test public void testNoConsumerInterface() { PojoReferenceMeta pojoReferenceMeta = new PojoReferenceMeta(); pojoReferenceMeta.setMicroserviceName("test"); pojoReferenceMeta.setSchemaId("schemaId"); try { pojoReferenceMeta.afterPropertiesSet(); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals( "microserviceName=test, schemaid=schemaId, \n" + "do not support implicit interface anymore, \n" + "because that caused problems:\n" + " 1.the startup process relies on other microservices\n" + " 2.cyclic dependent microservices can not be deployed\n" + "suggest to use @RpcReference or " + ".", e.getMessage()); } } } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/reference/TestRpcReferenceProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.reference; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.foundation.test.scaffolding.spring.SpringUtils; import org.apache.servicecomb.provider.pojo.Person; import org.apache.servicecomb.provider.pojo.PersonReference; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.env.Environment; public class TestRpcReferenceProcessor { static Environment environment = Mockito.mock(Environment.class); static ConfigurableBeanFactory beanFactory = Mockito.mock(ConfigurableBeanFactory.class); RpcReferenceProcessor consumers = new RpcReferenceProcessor(beanFactory); @BeforeEach public void setUp() { } @AfterEach public void teardown() { } @Test public void postProcessAfterInitialization() { Object bean = new Object(); Assertions.assertSame(bean, consumers.postProcessAfterInitialization(bean, "test")); } @Test public void testReference() { SCBEngine scbEngine = SCBBootstrap.createSCBEngineForTest(environment); PersonReference bean = new PersonReference(); Assertions.assertNull(bean.person); Mockito.when(beanFactory.resolveEmbeddedValue("test")).thenReturn("test"); Assertions.assertSame(bean, consumers.postProcessBeforeInitialization(bean, "id")); Assertions.assertNotNull(bean.person); scbEngine.destroy(); } @Test public void testNoReference() { Person bean = new Person(); Assertions.assertNull(bean.name); Assertions.assertSame(bean, consumers.postProcessBeforeInitialization(bean, "id")); Assertions.assertNull(bean.name); } @Test public void ensureNoInject() { SpringUtils.ensureNoInject(RpcReferenceProcessor.class); } } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/schema/TestPojoProducers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.schema; import org.apache.servicecomb.foundation.test.scaffolding.spring.SpringUtils; import org.apache.servicecomb.provider.pojo.IPerson; import org.apache.servicecomb.provider.pojo.Person; import org.apache.servicecomb.provider.pojo.RpcSchema; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestPojoProducers { PojoProducers producer = new PojoProducers(); @Test public void postProcessBeforeInitialization() { Object bean = new Object(); Assertions.assertSame(bean, producer.postProcessBeforeInitialization(bean, "test")); } @Test public void testPojoProducers() { Person bean = new Person(); Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "test")); Assertions.assertEquals(producer.getProducerMetas().size(), 1); } @Test public void testPojoProducersSchemaNull() { IPerson bean = new IPerson() { }; Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "test")); Assertions.assertEquals(producer.getProducerMetas().size(), 0); } @RpcSchema static class PersonEmptySchema implements IPerson { } @Test public void testPojoProducersSchemaIdNull() { IPerson bean = new PersonEmptySchema(); Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "test")); Assertions.assertEquals(producer.getProducerMetas().size(), 1); } @Test public void ensureNoInject() { SpringUtils.ensureNoInject(PojoProducers.class); } } ================================================ FILE: providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/schema/TestPojoSchemaMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.pojo.schema; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestPojoSchemaMeta { PojoProducerMeta lPojoSchemaMeta = null; @BeforeEach public void setUp() throws Exception { lPojoSchemaMeta = new PojoProducerMeta(); } @AfterEach public void tearDown() throws Exception { lPojoSchemaMeta = null; } @Test public void testGetImplementation() { PojoProducers producers = Mockito.mock(PojoProducers.class); lPojoSchemaMeta.setImplementation("implementation"); lPojoSchemaMeta.setPojoProducers(producers); lPojoSchemaMeta.afterPropertiesSet(); Assertions.assertEquals("implementation", lPojoSchemaMeta.getImplementation()); } @Test public void testGetInstance() throws Exception { Object lObject = new Object(); lPojoSchemaMeta.setInstance(lObject); Assertions.assertEquals(lObject, lPojoSchemaMeta.getInstance()); } @Test public void testGetSchemaId() throws Exception { lPojoSchemaMeta.setSchemaId("schemaId"); Assertions.assertEquals("schemaId", lPojoSchemaMeta.getSchemaId()); } } ================================================ FILE: providers/provider-pojo/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: filter-chains: consumer: default: empty producer: default: empty ================================================ FILE: providers/provider-rest-common/pom.xml ================================================ 4.0.0 org.apache.servicecomb providers 3.4.0-SNAPSHOT provider-rest-common Java Chassis::Providers::Rest Common org.apache.servicecomb common-rest org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding org.mockito mockito-inline test ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/InvocationToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; import io.vertx.core.net.SocketAddress; /** * when transport is not over http, mock an HttpServletRequest from Invocation */ public class InvocationToHttpServletRequest extends AbstractHttpServletRequest { private final RestOperationMeta swaggerOperation; private final Invocation invocation; public InvocationToHttpServletRequest(Invocation invocation) { this.swaggerOperation = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); this.invocation = invocation; } private SocketAddress getSockerAddress() { return (SocketAddress) invocation.getHandlerContext().get(CoreConst.REMOTE_ADDRESS); } @Override public String getParameter(String name) { RestParam param = swaggerOperation.getParamByName(name); if (param == null) { return null; } Object value = param.getValue(invocation.getSwaggerArguments()); if (value == null) { return null; } return String.valueOf(value); } @Override public String[] getParameterValues(String name) { RestParam param = swaggerOperation.getParamByName(name); if (param == null) { return null; } return param.getValueAsStrings(invocation.getSwaggerArguments()); } @Override public Map getParameterMap() { Map paramMap = new HashMap<>(); for (RestParam param : swaggerOperation.getParamList()) { String[] value = param.getValueAsStrings(invocation.getSwaggerArguments()); paramMap.put(param.getParamName(), value); } return paramMap; } @Override public String getHeader(String name) { return getParameter(name); } @Override public int getIntHeader(String name) { String header = getHeader(name); if (header == null) { return -1; } return Integer.parseInt(header); } @Override public String getMethod() { return this.swaggerOperation.getHttpMethod(); } @Override public String getPathInfo() { try { return this.swaggerOperation.getPathBuilder().createPathString(invocation.getSwaggerArguments()); } catch (Exception e) { throw new ServiceCombException("Failed to get path info.", e); } } @Override public String getRemoteAddr() { return this.getSockerAddress() == null ? "" : this.getSockerAddress().host(); } @Override public String getRemoteHost() { return this.getSockerAddress() == null ? "" : this.getSockerAddress().host(); } @Override public int getRemotePort() { return this.getSockerAddress() == null ? 0 : this.getSockerAddress().port(); } @Override public String getContextPath() { return ""; } /** * it's a mock httpServletRequest, contentType is unknown * @return contentType */ @Override public String getContentType() { return null; } /** * it's a mock httpServletRequest, characterEncoding is unknown * @return characterEncoding */ @Override public String getCharacterEncoding() { return null; } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; public class ProducerHttpRequestArgMapper extends AbstractProducerContextArgMapper { public ProducerHttpRequestArgMapper(String invocationArgumentName, String swaggerArgumentName) { super(invocationArgumentName, swaggerArgumentName); } @Override public Object createContextArg(SwaggerInvocation swaggerInvocation) { Invocation invocation = (Invocation) swaggerInvocation; // 从rest transport来 HttpServletRequest request = (HttpServletRequest) invocation.getHandlerContext().get(RestConst.REST_REQUEST); if (request != null) { return request; } // 通过args模拟request return new InvocationToHttpServletRequest(invocation); } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; public class ProducerHttpRequestArgMapperFactory implements ProducerContextArgumentMapperFactory { @Override public Class getContextClass() { return HttpServletRequest.class; } @Override public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { return new ProducerHttpRequestArgMapper(invocationArgumentName, swaggerArgumentName); } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; import io.vertx.core.http.ServerWebSocket; public class ProducerServerWebSocketArgMapperFactory implements ProducerContextArgumentMapperFactory { @Override public Class getContextClass() { return ServerWebSocket.class; } @Override public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { return new ProducerServerWebSocketMapper(invocationArgumentName, swaggerArgumentName); } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import org.apache.servicecomb.common.rest.WebSocketTransportContext; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; public class ProducerServerWebSocketMapper extends AbstractProducerContextArgMapper { public ProducerServerWebSocketMapper(String invocationArgumentName, String swaggerArgumentName) { super(invocationArgumentName, swaggerArgumentName); } @Override public Object createContextArg(SwaggerInvocation invocation) { WebSocketTransportContext context = invocation.getTransportContext(); return context.getServerWebSocket(); } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ProviderRestCommonConfiguration { @Bean public static RestProducers restProducers() { return new RestProducers(); } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducerProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import java.util.List; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.provider.producer.AbstractProducerProvider; import org.apache.servicecomb.core.provider.producer.ProducerMeta; import org.apache.servicecomb.foundation.common.utils.BeanUtils; public class RestProducerProvider extends AbstractProducerProvider { @Override public String getName() { return RestConst.REST; } @Override public List init() { // for some UT case, there is no spring context if (BeanUtils.getContext() == null) { return null; } RestProducers restProducers = BeanUtils.getContext().getBean(RestProducers.class); return restProducers.getProducerMetaList(); } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.provider.producer.ProducerMeta; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public class RestProducers implements BeanPostProcessor, EnvironmentAware { private final List producerMetaList = new ArrayList<>(); @SuppressWarnings("unchecked") private final Class restControllerCls = (Class) ReflectUtils .getClassByName("org.springframework.web.bind.annotation.RestController"); public List getProducerMetaList() { return producerMetaList; } private Environment environment; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { processProvider(beanName, bean); return bean; } protected void processProvider(String beanName, Object bean) { // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class // 所以,需要先取出原class,再取标注 Class beanCls = BeanUtils.getImplClassFromBean(bean); if (beanCls == null) { return; } RestSchema restSchema = beanCls.getAnnotation(RestSchema.class); if (restSchema != null) { ProducerMeta producerMeta = new ProducerMeta(restSchema.schemaId(), bean); producerMeta.setSchemaInterface(restSchema.schemaInterface()); producerMetaList.add(producerMeta); return; } if (restControllerCls != null && environment.getProperty(RestConst.PROVIDER_SCAN_REST_CONTROLLER, boolean.class, true) && beanCls.getAnnotation(restControllerCls) != null) { ProducerMeta producerMeta = new ProducerMeta(beanCls.getName(), bean); producerMetaList.add(producerMeta); } } @Override public void setEnvironment(Environment environment) { this.environment = environment; } } ================================================ FILE: providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import org.springframework.stereotype.Component; @Inherited @Documented @Retention(RUNTIME) @Target({TYPE, ANNOTATION_TYPE}) @Component public @interface RestSchema { String schemaId(); Class schemaInterface() default Object.class; } ================================================ FILE: providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.provider.rest.common.RestProducerProvider ================================================ FILE: providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.provider.rest.common.ProducerHttpRequestArgMapperFactory org.apache.servicecomb.provider.rest.common.ProducerServerWebSocketArgMapperFactory ================================================ FILE: providers/provider-rest-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.provider.rest.common.ProviderRestCommonConfiguration ================================================ FILE: providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestInvocationToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import io.vertx.core.net.SocketAddress; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestInvocationToHttpServletRequest { Invocation invocation = Mockito.mock(Invocation.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); RestOperationMeta swaggerOperation = Mockito.mock(RestOperationMeta.class); Map args; SocketAddress socketAddress = Mockito.mock(SocketAddress.class); Map handlerContext = new HashMap<>(); HttpServletRequest request; @BeforeEach public void setup() { handlerContext.put(CoreConst.REMOTE_ADDRESS, socketAddress); args = new HashMap<>(); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); request = new InvocationToHttpServletRequest(invocation); } @AfterEach public void tearDown() { } @Test public void testGetParameterNotFound() { Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(null); Assertions.assertNull(request.getParameter("name")); } @Test public void testGetParameterNull() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValue(args)).thenReturn(null); Assertions.assertNull(request.getParameter("name")); } @Test public void testGetParameterNormal() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValue(args)).thenReturn("value"); Assertions.assertEquals("value", request.getParameter("name")); } @Test public void testGetParameterValuesNotFound() { Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(null); Assertions.assertNull(request.getParameterValues("name")); } @Test public void testGetParameterValuesNormal() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValueAsStrings(args)).thenReturn(new String[] {"value"}); MatcherAssert.assertThat(request.getParameterValues("name"), Matchers.arrayContaining("value")); } @Test public void testGetParameterMap() { RestParam p1 = Mockito.mock(RestParam.class); RestParam p2 = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamList()).thenReturn(Arrays.asList(p1, p2)); Mockito.when(p1.getValueAsStrings(args)).thenReturn(new String[] {"v1"}); Mockito.when(p1.getParamName()).thenReturn("p1"); Mockito.when(p2.getValueAsStrings(args)).thenReturn(new String[] {"v2"}); Mockito.when(p2.getParamName()).thenReturn("p2"); Map params = request.getParameterMap(); MatcherAssert.assertThat(params.size(), Matchers.is(2)); MatcherAssert.assertThat(params, Matchers.hasEntry("p1", new String[] {"v1"})); MatcherAssert.assertThat(params, Matchers.hasEntry("p2", new String[] {"v2"})); } @Test public void testGetHeader() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValue(args)).thenReturn("value"); Assertions.assertEquals("value", request.getHeader("name")); } @Test public void testGetIntHeaderNotFound() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValue(args)).thenReturn(null); Assertions.assertEquals(-1, request.getIntHeader("name")); } @Test public void testGetIntHeaderNotNumber() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValue(args)).thenReturn("value"); try { request.getIntHeader("name"); Assertions.fail("must throw exception"); } catch (NumberFormatException e) { Assertions.assertEquals("For input string: \"value\"", e.getMessage()); } } @Test public void testGetIntHeaderNormal() { RestParam restParam = Mockito.mock(RestParam.class); Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); Mockito.when(restParam.getValue(args)).thenReturn("1"); Assertions.assertEquals(1, request.getIntHeader("name")); } @Test public void testGetMethod() { Mockito.when(swaggerOperation.getHttpMethod()).thenReturn("GET"); Assertions.assertEquals("GET", request.getMethod()); } @Test public void testGetPathInfoNormal() throws Exception { URLPathBuilder builder = Mockito.mock(URLPathBuilder.class); Mockito.when(swaggerOperation.getPathBuilder()).thenReturn(builder); Mockito.when(builder.createPathString(args)).thenReturn("/path"); Assertions.assertEquals("/path", request.getPathInfo()); } @Test public void testGetPathInfoException() throws Exception { URLPathBuilder builder = Mockito.mock(URLPathBuilder.class); Mockito.when(swaggerOperation.getPathBuilder()).thenReturn(builder); Mockito.when(builder.createPathString(args)).thenThrow(new Exception("error")); try { request.getPathInfo(); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals("Failed to get path info.", e.getMessage()); Assertions.assertEquals("error", e.getCause().getMessage()); } } @Test public void testGetRemoteAddress() throws Exception { Mockito.when(socketAddress.host()).thenReturn("127.0.0.2"); Mockito.when(socketAddress.port()).thenReturn(8088); Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); String addr = request.getRemoteAddr(); String host = request.getRemoteHost(); int port = request.getRemotePort(); Assertions.assertEquals(addr, "127.0.0.2"); Assertions.assertEquals(host, "127.0.0.2"); Assertions.assertEquals(port, 8088); } @Test public void testGetRemoteAddressEmpty() throws Exception { Invocation invocation = Mockito.mock(Invocation.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); handlerContext.remove(CoreConst.REMOTE_ADDRESS); Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); InvocationToHttpServletRequest request = new InvocationToHttpServletRequest(invocation); String addr = request.getRemoteAddr(); String host = request.getRemoteHost(); int port = request.getRemotePort(); Assertions.assertEquals(addr, ""); Assertions.assertEquals(host, ""); Assertions.assertEquals(port, 0); } @Test public void testGetContextPath() { InvocationToHttpServletRequest request = new InvocationToHttpServletRequest(invocation); Assertions.assertEquals("", request.getContextPath()); } @Test public void getContentType() { Assertions.assertNull(request.getContentType()); } @Test public void getCharacterEncoding() { Assertions.assertNull(request.getCharacterEncoding()); } } ================================================ FILE: providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestProducerHttpRequestArgMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import java.util.HashMap; import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestProducerHttpRequestArgMapper { Invocation invocation = Mockito.mock(Invocation.class); ProducerHttpRequestArgMapper mapper = new ProducerHttpRequestArgMapper("test", "test"); @Test public void testGetFromContext() { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Map context = new HashMap<>(); context.put(RestConst.REST_REQUEST, request); Mockito.when(invocation.getHandlerContext()).thenReturn(context); Assertions.assertSame(request, mapper.createContextArg(invocation)); } @Test public void testCreateFromInvocation() { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); RestOperationMeta swaggerOperation = Mockito.mock(RestOperationMeta.class); Map context = new HashMap<>(); Mockito.when(invocation.getHandlerContext()).thenReturn(context); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); Assertions.assertEquals(InvocationToHttpServletRequest.class, mapper.createContextArg(invocation).getClass()); } } ================================================ FILE: providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestProducers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.test.scaffolding.spring.SpringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestRestProducers { @RestSchema(schemaId = "test") public static class RestSchemaForTest { } static Environment environment = Mockito.mock(Environment.class); @BeforeAll public static void setUpClass() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty(RestConst.PROVIDER_SCAN_REST_CONTROLLER, boolean.class, true)) .thenReturn(true); } RestProducers producer = new RestProducers(); @Test public void postProcessBeforeInitialization() { Object bean = new Object(); Assertions.assertSame(bean, producer.postProcessBeforeInitialization(bean, "test")); } @Test public void postProcessAfterInitializationNormal() { RestSchemaForTest bean = new RestSchemaForTest(); Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "")); Assertions.assertEquals(1, producer.getProducerMetaList().size()); } @Test public void postProcessAfterInitializationNoAnnotation() { Object bean = new Object(); Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "")); Assertions.assertEquals(0, producer.getProducerMetaList().size()); } @Test public void ensureNoInject() { SpringUtils.ensureNoInject(RestProducers.class); } } ================================================ FILE: providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestServiceProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.rest.common; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; public class TestRestServiceProvider { @Test public void testInit() { ApplicationContext context = Mockito.mock(ApplicationContext.class); Mockito.when(context.getBean(RestProducers.class)).thenReturn(new RestProducers()); try (MockedStatic beanUtilsMockedStatic = Mockito.mockStatic(BeanUtils.class)) { beanUtilsMockedStatic.when(BeanUtils::getContext).thenReturn(context); RestProducerProvider restProducerProvider = new RestProducerProvider(); restProducerProvider.init(); Assertions.assertEquals(RestConst.REST, restProducerProvider.getName()); } } } ================================================ FILE: providers/provider-springmvc/pom.xml ================================================ 4.0.0 org.apache.servicecomb providers 3.4.0-SNAPSHOT provider-springmvc Java Chassis::Providers::Spring MVC org.apache.servicecomb provider-rest-common org.apache.servicecomb registry-local test org.apache.servicecomb swagger-invocation-springmvc org.apache.servicecomb swagger-generator-springmvc com.google.code.findbugs jsr305 org.apache.servicecomb foundation-test-scaffolding test org.apache.logging.log4j log4j-slf4j-impl test org.mockito mockito-inline test ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.net.URI; import org.springframework.web.client.RestTemplate; public abstract class AcceptableRestTemplate extends RestTemplate { public abstract boolean isAcceptable(String uri); public abstract boolean isAcceptable(URI uri); } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import static org.apache.servicecomb.foundation.common.utils.PartUtils.getSinglePart; import java.io.IOException; import java.net.HttpCookie; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; import com.google.common.annotations.VisibleForTesting; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; // restTemplate convert parameters to invocation args. public class CommonToHttpServletRequest extends AbstractHttpServletRequest { private final Map> queryParams; private final Map> httpHeaders; //contains all the file key in the parts private List fileKeys = new ArrayList<>(); // gen by httpHeaders private Cookie[] cookies; @SuppressWarnings("unchecked") public CommonToHttpServletRequest(Map pathParams, Map> queryParams, Map> httpHeaders, Object bodyObject, List fileKeys) { setAttribute(RestConst.PATH_PARAMETERS, pathParams); this.fileKeys = fileKeys; setAttribute(RestConst.BODY_PARAMETER, bodyObject); this.queryParams = queryParams; this.httpHeaders = httpHeaders; } @SuppressWarnings("unchecked") public CommonToHttpServletRequest(Map pathParams, Map> queryParams, Map> httpHeaders, Object bodyObject) { this(pathParams, queryParams, httpHeaders, bodyObject, null); } @Override public String getContentType() { return getHeader(HttpHeaders.CONTENT_TYPE); } // not include form data, only for query @Override public String getParameter(String name) { List queryValues = queryParams.get(name); if (queryValues == null || queryValues.isEmpty()) { return null; } return queryValues.get(0); } // not include form data, only for query @Override public String[] getParameterValues(String name) { List queryValues = queryParams.get(name); if (queryValues == null || queryValues.isEmpty()) { return new String[0]; } return queryValues.toArray(new String[0]); } @Override public String getHeader(String name) { List headerValues = httpHeaders.get(name); if (headerValues == null || headerValues.isEmpty()) { return null; } return headerValues.get(0); } @Override public Enumeration getHeaders(String name) { List headerValues = httpHeaders.get(name); if (headerValues == null || headerValues.isEmpty()) { return Collections.emptyEnumeration(); } return Collections.enumeration(headerValues); } @Override public Enumeration getHeaderNames() { return Collections.enumeration(httpHeaders.keySet()); } @Override public Cookie[] getCookies() { if (cookies == null) { cookies = createCookies(); } return cookies; } private Cookie[] createCookies() { List strCookies = httpHeaders.get(HttpHeaders.COOKIE); if (strCookies == null) { return new Cookie[] {}; } List result = new ArrayList<>(); for (String strCookie : strCookies) { List httpCookies = HttpCookie.parse(strCookie); for (HttpCookie httpCookie : httpCookies) { Cookie cookie = new Cookie(httpCookie.getName(), httpCookie.getValue()); result.add(cookie); } } return result.toArray(new Cookie[0]); } @Override public ServletInputStream getInputStream() throws IOException { return null; } @Override public void setHeader(String name, String value) { httpHeaders.put(name, Arrays.asList(value)); } @Override public void addHeader(String name, String value) { List list = httpHeaders.computeIfAbsent(name, key -> new ArrayList<>()); list.add(value); } @Override public Part getPart(String name) { Object value = findPartInputValue(name); return getSinglePart(name, value); } @Override public Collection getParts() { @SuppressWarnings("unchecked") Map form = (Map) getAttribute(RestConst.BODY_PARAMETER); List partList = new ArrayList<>(); filePartListWithForm(partList, form); return partList; } private void filePartListWithForm(List partList, Map form) { for (String key : fileKeys) { Object value = form.get(key); if (value == null) { continue; } if (Collection.class.isInstance(value)) { Collection collection = (Collection) value; for (Object part : collection) { partList.add(getSinglePart(key, part)); } continue; } if (value.getClass().isArray()) { Object[] params = (Object[]) value; for (Object param : params) { partList.add(getSinglePart(key, param)); } continue; } partList.add(getSinglePart(key, value)); } } protected Object findPartInputValue(String name) { @SuppressWarnings("unchecked") Map form = (Map) getAttribute(RestConst.BODY_PARAMETER); Object value = form.get(name); if (value == null) { return null; } if (Collection.class.isInstance(value)) { Collection collection = (Collection) value; if (collection.isEmpty()) { return null; } value = collection.iterator().next(); } return value; } @VisibleForTesting public List getFileKeys() { return fileKeys; } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.OutputStream; import java.lang.reflect.Type; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestCodec; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; import com.fasterxml.jackson.databind.type.TypeFactory; import com.google.common.annotations.VisibleForTesting; import io.netty.handler.codec.http.QueryStringDecoder; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.Response.Status; public class CseClientHttpRequest implements ClientHttpRequest { // URL format:cse://microserviceName/business url private URI uri; // business url private String path; private HttpMethod method; private HttpHeaders httpHeaders = new HttpHeaders(); private InvocationContext context; private Object requestBody; private Map> queryParams; private RequestMeta requestMeta; private Type responseType; public CseClientHttpRequest() { } public CseClientHttpRequest(URI uri, HttpMethod method) { this.uri = uri; this.method = method; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } protected RequestMeta getRequestMeta() { return requestMeta; } @VisibleForTesting public void setRequestMeta(RequestMeta requestMeta) { this.requestMeta = requestMeta; } public void setUri(URI uri) { this.uri = uri; } public void setMethod(HttpMethod method) { this.method = method; } protected void setQueryParams(Map> queryParams) { this.queryParams = queryParams; } public InvocationContext getContext() { return context; } public void setContext(InvocationContext context) { this.context = context; } public Type getResponseType() { return responseType; } public void setResponseType(Type responseType) { this.responseType = responseType; } public void setRequestBody(Object requestBody) { this.requestBody = requestBody; } public void setHttpHeaders(HttpHeaders headers) { if (headers != null) { this.httpHeaders = headers; } } @Override public HttpMethod getMethod() { return method; } @Override public URI getURI() { return uri; } @Override public Map getAttributes() { return Collections.emptyMap(); } @Override public HttpHeaders getHeaders() { return httpHeaders; } @Override public OutputStream getBody() { return null; } @Override public ClientHttpResponse execute() { path = findUriPath(uri); requestMeta = createRequestMeta(method.name(), uri); QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri.getRawSchemeSpecificPart()); queryParams = queryStringDecoder.parameters(); Map swaggerArguments = this.collectArguments(); // 异常流程,直接抛异常出去 return this.invoke(swaggerArguments); } protected RequestMeta createRequestMeta(String httpMethod, URI uri) { String microserviceName = parseMicroserviceName(uri); MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance() .getOrCreateReferenceConfig(microserviceName); if (microserviceReferenceConfig == null) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, new CommonExceptionData(String.format("Failed to invoke service %s. Maybe service" + " not registered or no active instance.", microserviceName))); } MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getMicroserviceMeta(); ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); if (servicePathManager == null) { throw new Error(String.format("no schema defined for %s:%s", microserviceMeta.getAppId(), microserviceMeta.getMicroserviceName())); } OperationLocator locator = servicePathManager.consumerLocateOperation(path, httpMethod); RestOperationMeta swaggerRestOperation = locator.getOperation(); OperationMeta operationMeta = locator.getOperation().getOperationMeta(); ReferenceConfig referenceConfig = microserviceReferenceConfig.createReferenceConfig(operationMeta); Map pathParams = locator.getPathVarMap(); return new RequestMeta(referenceConfig, swaggerRestOperation, pathParams); } private String parseMicroserviceName(URI uri) { String microserviceName = uri.getAuthority(); return microserviceName.replace(CseUriTemplateHandler.APP_SERVICE_SEPARATOR_INTERNAL, DefinitionConst.APP_SERVICE_SEPARATOR); } protected String findUriPath(URI uri) { return uri.getRawPath(); } protected Invocation prepareInvocation(Map swaggerArguments) { Invocation invocation = InvocationFactory.forConsumer(requestMeta.getReferenceConfig(), requestMeta.getOperationMeta(), requestMeta.getOperationMeta().buildBaseConsumerRuntimeType(), swaggerArguments); invocation.getHandlerContext().put(RestConst.REST_CLIENT_REQUEST_PATH, path + (this.uri.getRawQuery() == null ? "" : "?" + this.uri.getRawQuery())); if (context != null) { invocation.addContext(context.getContext()); invocation.addLocalContext(context.getLocalContext()); } if (responseType != null && !(responseType instanceof Class && Part.class.isAssignableFrom((Class) responseType))) { invocation.setSuccessResponseType(TypeFactory.defaultInstance().constructType(responseType)); } invocation.getHandlerContext().put(RestConst.CONSUMER_HEADER, httpHeaders.toSingleValueMap()); return invocation; } @VisibleForTesting CseClientHttpResponse invoke(Map swaggerArguments) { Invocation invocation = prepareInvocation(swaggerArguments); Response response = doInvoke(invocation); if (response.isSucceed()) { return new CseClientHttpResponse(response); } throw ExceptionFactory.convertConsumerException(response.getResult()); } protected Response doInvoke(Invocation invocation) { return InvokerUtils.innerSyncInvoke(invocation); } protected Map collectArguments() { HttpServletRequest mockRequest = new CommonToHttpServletRequest(requestMeta.getPathParams(), queryParams, httpHeaders, requestBody, requestMeta.getSwaggerRestOperation().getFileKeys()); // no types info, so will not convert any parameters return RestCodec.restToArgs(mockRequest, requestMeta.getSwaggerRestOperation()); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.net.URI; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; public class CseClientHttpRequestFactory implements ClientHttpRequestFactory { @Override public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { return new CseClientHttpRequest(uri, httpMethod); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.io.InputStream; import java.util.Map.Entry; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; import io.vertx.core.MultiMap; /** * cse应答在transport层已经完成了码流到对象的转换 * 这里是适配springmvc的机制,让调用者能拿到已经转换好的对象 */ public class CseClientHttpResponse implements ClientHttpResponse { // 让springmvc client以为应答有body // mark、reset都是有锁的,这里通过重写取消了锁 private static final InputStream BODY_INPUT_STREAM = new InputStream() { @Override public boolean markSupported() { return true; } @Override public void mark(int readlimit) { } @Override public int read() throws IOException { return 0; } @Override public void reset() throws IOException { } }; private final Response response; private HttpHeaders httpHeaders; public CseClientHttpResponse(Response response) { this.response = response; } public Object getResult() { return response.getResult(); } @Override public InputStream getBody() throws IOException { return BODY_INPUT_STREAM; } @Override public HttpHeaders getHeaders() { if (httpHeaders == null) { HttpHeaders tmpHeaders = new HttpHeaders(); MultiMap headers = response.getHeaders(); if (headers != null) { for (Entry entry : headers.entries()) { tmpHeaders.add(entry.getKey(), entry.getValue()); } } httpHeaders = tmpHeaders; } return httpHeaders; } @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.valueOf(response.getStatusCode()); } @Override @Deprecated(since = "6.0") @SuppressWarnings("deprecations") public int getRawStatusCode() throws IOException { return response.getStatusCode(); } @Override public String getStatusText() throws IOException { return response.getReasonPhrase(); } @Override public void close() { } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpEntity.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.springframework.http.HttpEntity; import org.springframework.util.MultiValueMap; public class CseHttpEntity extends HttpEntity { private InvocationContext context; public CseHttpEntity(T body) { super(body); } public CseHttpEntity(MultiValueMap headers) { super(headers); } public CseHttpEntity(T body, MultiValueMap headers) { super(body, headers); } /** * 获取context的值 * @return 返回 context */ public InvocationContext getContext() { return context; } /** * 对context进行赋值 * @param context context的新值 */ public void setContext(InvocationContext context) { this.context = context; } public void addContext(String key, String value) { if (context == null) { context = new InvocationContext(); } context.addContext(key, value); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.lang.Nullable; public class CseHttpMessageConverter implements GenericHttpMessageConverter { private static final List ALL_MEDIA_TYPE = Arrays.asList(MediaType.ALL); @Override public boolean canRead(Class clazz, MediaType mediaType) { return true; } @Override public boolean canWrite(Class clazz, MediaType mediaType) { return true; } @Override public List getSupportedMediaTypes() { return ALL_MEDIA_TYPE; } @Override public Object read(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException { throw new IllegalStateException("not supported"); } private Object read(HttpInputMessage inputMessage) { throw new IllegalStateException("not supported"); } @Override public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { write(o, outputMessage); } private void write(Object o, HttpOutputMessage outputMessage) { CseClientHttpRequest request = (CseClientHttpRequest) outputMessage; request.setRequestBody(o); } @Override public boolean canRead(Type type, @Nullable Class contextClass, @Nullable MediaType mediaType) { return true; } @Override public Object read(Type type, @Nullable Class contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { throw new IllegalStateException("not supported"); } @Override public boolean canWrite(@Nullable Type type, Class clazz, @Nullable MediaType mediaType) { return true; } @Override public void write(Object o, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { write(o, outputMessage); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverterExtractor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.ResponseExtractor; public class CseHttpMessageConverterExtractor implements ResponseExtractor { @Override @SuppressWarnings("unchecked") public T extractData(ClientHttpResponse response) throws IOException { return (T) ((CseClientHttpResponse) response).getResult(); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.lang.reflect.Type; import org.apache.servicecomb.foundation.common.utils.GenericsUtils; import org.springframework.http.client.ClientHttpRequest; import org.springframework.web.client.RequestCallback; public class CseRequestCallback implements RequestCallback { private final Object requestBody; private final RequestCallback targetCallback; private final Type responseType; public CseRequestCallback(Object requestBody, RequestCallback targetCallback, Type responseType) { this.requestBody = requestBody; this.targetCallback = targetCallback; this.responseType = responseType; } /** * {@inheritDoc} */ @Override public void doWithRequest(ClientHttpRequest request) throws IOException { targetCallback.doWithRequest(request); CseClientHttpRequest req = (CseClientHttpRequest) request; req.setResponseType(overrideResponseType()); if (!CseHttpEntity.class.isInstance(requestBody)) { return; } CseHttpEntity entity = (CseHttpEntity) requestBody; req.setContext(entity.getContext()); } private Type overrideResponseType() { if (GenericsUtils.isGenericResponseType(responseType)) { // code: List response = restTemplate // .postForObject("/testListObjectParam", request, List.class); // will using server schema type to deserialize return null; } // code: MyObject response = .postForObject("/testListObjectParam", request, MyObject.class); return responseType; } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseResponseEntityResponseExtractor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.lang.reflect.Type; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpResponse; import org.springframework.lang.Nullable; import org.springframework.web.client.ResponseExtractor; public class CseResponseEntityResponseExtractor implements ResponseExtractor> { @Nullable private final CseHttpMessageConverterExtractor delegate; public CseResponseEntityResponseExtractor(@Nullable Type responseType) { if (responseType != null && Void.class != responseType) { this.delegate = new CseHttpMessageConverterExtractor<>(); } else { this.delegate = null; } } @Override public ResponseEntity extractData(ClientHttpResponse response) throws IOException { if (this.delegate != null) { T body = this.delegate.extractData(response); return ResponseEntity.status(response.getStatusCode().value()).headers(response.getHeaders()).body(body); } else { return ResponseEntity.status(response.getStatusCode().value()).headers(response.getHeaders()).build(); } } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.lang.reflect.Type; import java.net.URI; import java.util.Arrays; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestClientException; public class CseRestTemplate extends AcceptableRestTemplate { public CseRestTemplate() { setMessageConverters(Arrays.asList(new CseHttpMessageConverter())); setRequestFactory(new CseClientHttpRequestFactory()); setUriTemplateHandler(new CseUriTemplateHandler()); } // GET @Override @Nullable public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public T getForObject(String url, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public T getForObject(URI url, Class responseType) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.GET, requestCallback, responseExtractor); } // HEAD // no override // POST @Override @Nullable public T postForObject(String url, @Nullable Object request, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public T postForObject(String url, @Nullable Object request, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public T postForObject(URI url, @Nullable Object request, Class responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.POST, requestCallback, responseExtractor); } // PUT // no override // PATCH @Override @Nullable public T patchForObject(String url, @Nullable Object request, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public T patchForObject(String url, @Nullable Object request, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); } @Override @Nullable public T patchForObject(URI url, @Nullable Object request, Class responseType) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); CseHttpMessageConverterExtractor responseExtractor = new CseHttpMessageConverterExtractor<>(); return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor); } // DELETE // no override // OPTIONS // no override // exchange // no override @Override public ResponseExtractor> responseEntityExtractor(Type responseType) { return new CseResponseEntityResponseExtractor<>(responseType); } @Override public RequestCallback httpEntityCallback(Object requestBody) { RequestCallback callback = super.httpEntityCallback(requestBody); CseRequestCallback cseCallback = new CseRequestCallback(requestBody, callback, null); return cseCallback; } @Override public RequestCallback httpEntityCallback(Object requestBody, Type responseType) { RequestCallback callback = super.httpEntityCallback(requestBody, responseType); CseRequestCallback cseCallback = new CseRequestCallback(requestBody, callback, responseType); return cseCallback; } @Override public RequestCallback acceptHeaderRequestCallback(Class responseType) { RequestCallback callback = super.acceptHeaderRequestCallback(responseType); CseRequestCallback cseCallback = new CseRequestCallback(null, callback, responseType); return cseCallback; } @Override public boolean isAcceptable(String uri) { return uri.startsWith(RestConst.URI_PREFIX) || uri.startsWith(RestConst.URI_PREFIX_NEW); } @Override public boolean isAcceptable(URI uri) { return RestConst.SCHEME.equals(uri.getScheme()) || RestConst.SCHEME_NEW.equals(uri.getScheme()); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.net.URI; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.springframework.web.util.DefaultUriBuilderFactory; public class CseUriTemplateHandler extends DefaultUriBuilderFactory { // Must be one of URI unreserved `ALPHA / DIGIT / "-" / "." / "_" / "~"` public static final String APP_SERVICE_SEPARATOR_INTERNAL = "~"; public CseUriTemplateHandler() { } @Override public URI expand(String uriTemplate, Map uriVars) { return super.expand(parseUrl(uriTemplate), uriVars); } @Override public URI expand(String uriTemplate, Object... uriVars) { return super.expand(parseUrl(uriTemplate), uriVars); } private static String parseUrl(String uriTemplate) { int indexSchema = -1; if (uriTemplate.startsWith(RestConst.URI_PREFIX)) { indexSchema = RestConst.URI_PREFIX.length(); } if (uriTemplate.startsWith(RestConst.URI_PREFIX_NEW)) { indexSchema = RestConst.URI_PREFIX_NEW.length(); } if (indexSchema != -1) { int indexPath = uriTemplate.indexOf("/", indexSchema); String host = uriTemplate.substring(indexSchema, indexPath); host = host.replace(DefinitionConst.APP_SERVICE_SEPARATOR, APP_SERVICE_SEPARATOR_INTERNAL); return uriTemplate.substring(0, indexSchema) + host + uriTemplate.substring(indexPath); } return uriTemplate; } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RequestMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; /** * 封装每一次调用的元数据 */ public class RequestMeta { private final ReferenceConfig referenceConfig; private final OperationMeta operationMeta; private final RestOperationMeta swaggerRestOperation; private final Map pathParams; public RequestMeta(ReferenceConfig referenceConfig, RestOperationMeta swaggerRestOperation, Map pathParams) { this.referenceConfig = referenceConfig; this.operationMeta = swaggerRestOperation.getOperationMeta(); this.swaggerRestOperation = swaggerRestOperation; this.pathParams = pathParams; } public ReferenceConfig getReferenceConfig() { return referenceConfig; } public Map getPathParams() { return pathParams; } public RestOperationMeta getSwaggerRestOperation() { return swaggerRestOperation; } public OperationMeta getOperationMeta() { return operationMeta; } public String getOperationQualifiedName() { return operationMeta.getSchemaQualifiedName(); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import org.springframework.web.client.RestOperations; public final class RestTemplateBuilder { private static final RestTemplateWrapper wrapper = new RestTemplateWrapper(); private RestTemplateBuilder() { } public static RestOperations create() { return wrapper; } public static void addAcceptableRestTemplate(int index, AcceptableRestTemplate restTemplate) { wrapper.addAcceptableRestTemplate(index, restTemplate); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriTemplateHandler; /** * Wrapper class to support java chassis invocation and non java chassis invocation. */ class RestTemplateWrapper extends RestTemplate { private final List acceptableRestTemplates = new ArrayList<>(); final RestTemplate defaultRestTemplate = new RestTemplate(); RestTemplateWrapper() { acceptableRestTemplates.add(new CseRestTemplate()); } void addAcceptableRestTemplate(int index, AcceptableRestTemplate restTemplate) { acceptableRestTemplates.add(index, restTemplate); } RestTemplate getRestTemplate(String url) { for (AcceptableRestTemplate template : acceptableRestTemplates) { if (template.isAcceptable(url)) { return template; } } return defaultRestTemplate; } RestTemplate getRestTemplate(URI uri) { for (AcceptableRestTemplate template : acceptableRestTemplates) { if (template.isAcceptable(uri)) { return template; } } return defaultRestTemplate; } @Override public T getForObject(String url, Class responseType, Object... urlVariables) throws RestClientException { return getRestTemplate(url).getForObject(url, responseType, urlVariables); } @Override public T getForObject(String url, Class responseType, Map urlVariables) throws RestClientException { return getRestTemplate(url).getForObject(url, responseType, urlVariables); } @Override public T getForObject(URI url, Class responseType) throws RestClientException { return getRestTemplate(url).getForObject(url, responseType); } @Override public ResponseEntity getForEntity(String url, Class responseType, Object... urlVariables) throws RestClientException { return getRestTemplate(url).getForEntity(url, responseType, urlVariables); } @Override public ResponseEntity getForEntity(String url, Class responseType, Map urlVariables) throws RestClientException { return getRestTemplate(url).getForEntity(url, responseType, urlVariables); } @Override public ResponseEntity getForEntity(URI url, Class responseType) throws RestClientException { return getRestTemplate(url).getForEntity(url, responseType); } @Override public T postForObject(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException { return getRestTemplate(url).postForObject(url, request, responseType, uriVariables); } @Override public T postForObject(String url, Object request, Class responseType, Map uriVariables) throws RestClientException { return getRestTemplate(url).postForObject(url, request, responseType, uriVariables); } @Override public T postForObject(URI url, Object request, Class responseType) throws RestClientException { return getRestTemplate(url).postForObject(url, request, responseType); } @Override public ResponseEntity postForEntity(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException { return getRestTemplate(url).postForEntity(url, request, responseType, uriVariables); } @Override public ResponseEntity postForEntity(String url, Object request, Class responseType, Map uriVariables) throws RestClientException { return getRestTemplate(url).postForEntity(url, request, responseType, uriVariables); } @Override public ResponseEntity postForEntity(URI url, Object request, Class responseType) throws RestClientException { return getRestTemplate(url).postForEntity(url, request, responseType); } @Override public void put(String url, Object request, Object... urlVariables) throws RestClientException { getRestTemplate(url).put(url, request, urlVariables); } @Override public void put(String url, Object request, Map urlVariables) throws RestClientException { getRestTemplate(url).put(url, request, urlVariables); } @Override public void put(URI url, Object request) throws RestClientException { getRestTemplate(url).put(url, request); } @Override public void delete(String url, Object... urlVariables) throws RestClientException { getRestTemplate(url).delete(url, urlVariables); } @Override public void delete(String url, Map urlVariables) throws RestClientException { getRestTemplate(url).delete(url, urlVariables); } @Override public void delete(URI url) throws RestClientException { getRestTemplate(url).delete(url); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables) throws RestClientException { return getRestTemplate(url).exchange(url, method, requestEntity, responseType, uriVariables); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType, Map uriVariables) throws RestClientException { return getRestTemplate(url).exchange(url, method, requestEntity, responseType, uriVariables); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Map uriVariables) throws RestClientException { return getRestTemplate(url).exchange(url, method, requestEntity, responseType, uriVariables); } @Override public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType, Object... uriVariables) throws RestClientException { return getRestTemplate(url).exchange(url, method, requestEntity, responseType, uriVariables); } @Override public ResponseEntity exchange(RequestEntity requestEntity, Class responseType) throws RestClientException { return getRestTemplate(requestEntity.getUrl()).exchange(requestEntity, responseType); } @Override public ResponseEntity exchange(RequestEntity requestEntity, ParameterizedTypeReference responseType) throws RestClientException { return getRestTemplate(requestEntity.getUrl()).exchange(requestEntity, responseType); } @Override public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, Class responseType) throws RestClientException { return getRestTemplate(url).exchange(url, method, requestEntity, responseType); } @Override public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType) throws RestClientException { return getRestTemplate(url).exchange(url, method, requestEntity, responseType); } @Override public HttpHeaders headForHeaders(String url, Object... urlVariables) throws RestClientException { return getRestTemplate(url).headForHeaders(url, urlVariables); } @Override public HttpHeaders headForHeaders(String url, Map urlVariables) throws RestClientException { return getRestTemplate(url).headForHeaders(url, urlVariables); } @Override public HttpHeaders headForHeaders(URI url) throws RestClientException { return getRestTemplate(url).headForHeaders(url); } @Override public URI postForLocation(String url, Object request, Object... urlVariables) throws RestClientException { return getRestTemplate(url).postForLocation(url, request, urlVariables); } @Override public URI postForLocation(String url, Object request, Map urlVariables) throws RestClientException { return getRestTemplate(url).postForLocation(url, request, urlVariables); } @Override public URI postForLocation(URI url, Object request) throws RestClientException { return getRestTemplate(url).postForLocation(url, request); } @Override public Set optionsForAllow(String url, Object... urlVariables) throws RestClientException { return getRestTemplate(url).optionsForAllow(url, urlVariables); } @Override public Set optionsForAllow(String url, Map urlVariables) throws RestClientException { return getRestTemplate(url).optionsForAllow(url, urlVariables); } @Override public Set optionsForAllow(URI url) throws RestClientException { return getRestTemplate(url).optionsForAllow(url); } @Override public T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor, Object... urlVariables) throws RestClientException { return getRestTemplate(url).execute(url, method, requestCallback, responseExtractor, urlVariables); } @Override public T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor, Map urlVariables) throws RestClientException { return getRestTemplate(url).execute(url, method, requestCallback, responseExtractor, urlVariables); } @Override public T execute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException { return getRestTemplate(url).execute(url, method, requestCallback, responseExtractor); } @Override public void setInterceptors(List interceptors) { super.setInterceptors(interceptors); defaultRestTemplate.setInterceptors(interceptors); } @Override public void setRequestFactory(ClientHttpRequestFactory requestFactory) { super.setRequestFactory(requestFactory); defaultRestTemplate.setRequestFactory(requestFactory); } @Override public void setErrorHandler(ResponseErrorHandler errorHandler) { super.setErrorHandler(errorHandler); acceptableRestTemplates.forEach(template -> template.setErrorHandler(errorHandler)); defaultRestTemplate.setErrorHandler(errorHandler); } @Override public void setDefaultUriVariables(Map defaultUriVariables) { super.setDefaultUriVariables(defaultUriVariables); acceptableRestTemplates.forEach(template -> template.setDefaultUriVariables(defaultUriVariables)); defaultRestTemplate.setDefaultUriVariables(defaultUriVariables); } @Override public void setUriTemplateHandler(UriTemplateHandler handler) { super.setUriTemplateHandler(handler); defaultRestTemplate.setUriTemplateHandler(handler); } @Override public void setMessageConverters(List> messageConverters) { super.setMessageConverters(messageConverters); defaultRestTemplate.setMessageConverters(messageConverters); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestOperations; @Configuration class ServiceCombRestTemplateConfig { @Bean RestOperations restTemplate() { return RestTemplateBuilder.create(); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.net.URI; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; /** * When deploying in a container, like tomcat, users want to invoke service with full path, including container context * root and servlet path. */ public class UrlWithProviderPrefixClientHttpRequestFactory implements ClientHttpRequestFactory { static class UrlWithProviderPrefixClientHttpRequest extends CseClientHttpRequest { private final String prefix; public UrlWithProviderPrefixClientHttpRequest(URI uri, HttpMethod httpMethod, String prefix) { super(uri, httpMethod); this.prefix = prefix; } @Override protected String findUriPath(URI uri) { return uri.getRawPath().substring(prefix.length()); } } private final String prefix; public UrlWithProviderPrefixClientHttpRequestFactory(String prefix) { this.prefix = prefix; } @Override public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { return new UrlWithProviderPrefixClientHttpRequest(uri, httpMethod, prefix); } } ================================================ FILE: providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithServiceNameClientHttpRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.net.URI; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; // URL format:cse://business url // business url first part is microserviceName public class UrlWithServiceNameClientHttpRequestFactory implements ClientHttpRequestFactory { static class UrlWithServiceNameClientHttpRequest extends CseClientHttpRequest { public UrlWithServiceNameClientHttpRequest(URI uri, HttpMethod httpMethod) { super(uri, httpMethod); } @Override protected String findUriPath(URI uri) { return "/" + uri.getAuthority() + uri.getRawPath(); } } @Override public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { return new UrlWithServiceNameClientHttpRequest(uri, httpMethod); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import static org.hamcrest.core.IsInstanceOf.instanceOf; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; import org.springframework.web.client.RestOperations; public class ServiceCombRestTemplateConfigTest { private final ServiceCombRestTemplateConfig config = new ServiceCombRestTemplateConfig(); @Test public void exposesServiceCombRestTemplate() { RestOperations restTemplate = config.restTemplate(); MatcherAssert.assertThat(restTemplate, instanceOf(RestTemplateWrapper.class)); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; public class TestCommonToHttpServletRequest { @Test public void testConstructFormTrue() { Map forms = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, forms); Assertions.assertEquals(forms, request.getAttribute(RestConst.BODY_PARAMETER)); } @Test public void testConstructFormFalse() { Object body = new Object(); HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, body); Assertions.assertEquals(body, request.getAttribute(RestConst.BODY_PARAMETER)); } @Test public void testConstructNormal() { List fileKeys = new ArrayList<>(); fileKeys.add("test1"); fileKeys.add("test2"); HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, null, fileKeys); Assertions.assertEquals(2, ((CommonToHttpServletRequest) request).getFileKeys().size()); Assertions.assertEquals("test1", ((CommonToHttpServletRequest) request).getFileKeys().get(0)); Assertions.assertEquals("test2", ((CommonToHttpServletRequest) request).getFileKeys().get(1)); } @Test public void testConstructPath() { Map pathParams = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(pathParams, null, null, null); Assertions.assertEquals(pathParams, request.getAttribute(RestConst.PATH_PARAMETERS)); } @Test public void testGetContentType() { Map> httpHeaders = new HashMap<>(); httpHeaders.put(HttpHeaders.CONTENT_TYPE, Arrays.asList("json")); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertEquals("json", request.getContentType()); } @Test public void testGetParameterNormal() { Map> queryParams = new HashMap<>(); queryParams.put("name", Arrays.asList("value")); HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); Assertions.assertEquals("value", request.getParameter("name")); } @Test public void testGetParameterEmpty() { Map> queryParams = new HashMap<>(); queryParams.put("name", Arrays.asList()); HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); Assertions.assertNull(request.getParameter("name")); } @Test public void testGetParameterNull() { Map> queryParams = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); Assertions.assertNull(request.getParameter("name")); } @Test public void testGetParameterValuesNormal() { Map> queryParams = new HashMap<>(); queryParams.put("name", Arrays.asList("value")); HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); MatcherAssert.assertThat(request.getParameterValues("name"), Matchers.arrayContaining("value")); } @Test public void testGetParameterValuesEmpty() { Map> queryParams = new HashMap<>(); queryParams.put("name", Arrays.asList()); HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); Assertions.assertArrayEquals(new String[0], request.getParameterValues("name")); } @Test public void testGetParameterValuesNull() { Map> queryParams = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); Assertions.assertArrayEquals(new String[0], request.getParameterValues("name")); } @Test public void testGetHeaderNormal() { Map> httpHeaders = new HashMap<>(); httpHeaders.put("name", Arrays.asList("value")); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertEquals("value", request.getHeader("name")); } @Test public void testGetHeaderEmpty() { Map> httpHeaders = new HashMap<>(); httpHeaders.put("name", Arrays.asList()); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertNull(request.getHeader("name")); } @Test public void testGetHeaderNamesNormal() { Map> httpHeaders = new HashMap<>(); httpHeaders.put("name", Arrays.asList("value")); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); MatcherAssert.assertThat(Collections.list(request.getHeaderNames()), Matchers.contains("name")); } @Test public void testGetHeaderNamesEmpty() { Map> httpHeaders = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertFalse(request.getHeaderNames().hasMoreElements()); } @Test public void testGetHeaderNull() { Map> httpHeaders = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertNull(request.getHeader("name")); } @Test public void testGetHeadersNormal() { Map> httpHeaders = new HashMap<>(); httpHeaders.put("name", Arrays.asList("value")); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("value")); } @Test public void testGetHeadersEmpty() { Map> httpHeaders = new HashMap<>(); httpHeaders.put("name", Arrays.asList()); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertFalse(request.getHeaders("name").hasMoreElements()); } @Test public void testGetHeadersNull() { Map> httpHeaders = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertFalse(request.getHeaders("name").hasMoreElements()); } @Test public void testGetCookiesNull() { Map> httpHeaders = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Assertions.assertEquals(0, request.getCookies().length); } @Test public void testGetCookiesNormal() { Map> httpHeaders = new HashMap<>(); httpHeaders.put(HttpHeaders.COOKIE, Arrays.asList("k1=v1;k2=v2;")); HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); Cookie[] cookies = request.getCookies(); Assertions.assertSame(cookies, request.getCookies()); Assertions.assertEquals(1, cookies.length); Assertions.assertEquals("k1", cookies[0].getName()); Assertions.assertEquals("v1", cookies[0].getValue()); } @Test public void testGetInputStream() throws IOException { HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, null); Assertions.assertNull(request.getInputStream()); } @Test public void testSetHeader() { Map> httpHeaders = new HashMap<>(); HttpServletRequestEx request = new CommonToHttpServletRequest(null, null, httpHeaders, null); request.setHeader("name", "v1"); request.setHeader("name", "v2"); Assertions.assertEquals("v2", request.getHeader("name")); } @Test public void testAddHeader() { Map> httpHeaders = new HashMap<>(); HttpServletRequestEx request = new CommonToHttpServletRequest(null, null, httpHeaders, null); request.addHeader("name", "v1"); request.addHeader("name", "v2"); MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("v1", "v2")); } @Test public void testGetParts() { List restParams = new ArrayList<>(); restParams.add("test1"); restParams.add("test2"); File file1 = new File("file1.txt"); File file2 = new File("file2.txt"); File[] files = {file1, file2}; List list = Arrays.asList(files); Map objectMap = new HashMap<>(); objectMap.put("test1", list); objectMap.put("test2", files); objectMap.put("test3", list); objectMap.put("test4", "haha"); Map pathParams = new HashMap<>(); HttpServletRequest request = new CommonToHttpServletRequest(pathParams, null, null, objectMap, restParams); try { Collection tmpParts = request.getParts(); ArrayList parts = new ArrayList<>(tmpParts); Assertions.assertEquals(4, parts.size()); Assertions.assertEquals("test1", parts.get(0).getName()); Assertions.assertEquals("test1", parts.get(1).getName()); Assertions.assertEquals("test2", parts.get(2).getName()); Assertions.assertEquals("test2", parts.get(3).getName()); } catch (Throwable e) { Assertions.fail("should not throw exception"); } } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.net.URI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; public class TestCseClientHttpRequestFactory { @Test public void testCseClientHttpRequestFactory() { CseClientHttpRequestFactory lCseClientHttpRequestFactory = new CseClientHttpRequestFactory(); try { Assertions.assertEquals(HttpMethod.GET, lCseClientHttpRequestFactory.createRequest(URI.create("/test"), HttpMethod.GET).getMethod()); } catch (IOException e) { Assertions.assertNotNull(e); } } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; public class TestCseClientHttpResponse { private Object result; Response response = Response.ok(result); CseClientHttpResponse cseclientrequest = new CseClientHttpResponse(response); @Test public void testGetResult() { Assertions.assertNull(cseclientrequest.getResult()); } @Test public void testGetBody() throws IOException { Assertions.assertNotNull(cseclientrequest.getBody()); } @Test public void testGetHeaders() { Assertions.assertNotNull(cseclientrequest.getHeaders()); } @Test public void testGetStatusCode() throws IOException { Assertions.assertEquals(HttpStatus.OK, cseclientrequest.getStatusCode()); } @Test public void testGetRawStatusCode() throws IOException { Assertions.assertEquals(200, cseclientrequest.getRawStatusCode()); } @Test public void testGetStatusText() throws IOException { cseclientrequest.close(); Assertions.assertEquals(HttpStatus.OK.getReasonPhrase(), cseclientrequest.getStatusText()); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseHttpEntity.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCseHttpEntity { @Test public void test() { CseHttpEntity entity = new CseHttpEntity<>(null, null); entity.addContext("c1", "c1v"); Assertions.assertEquals(1, entity.getContext().getContext().size()); Assertions.assertEquals("c1v", entity.getContext().getContext("c1")); InvocationContext context = new InvocationContext(); context.addContext("c2", "c2v"); entity.setContext(context); Assertions.assertEquals(1, entity.getContext().getContext().size()); Assertions.assertEquals("c2v", entity.getContext().getContext("c2")); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRequestCallback.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.web.client.RequestCallback; public class TestCseRequestCallback { @Test public void testNormal() throws IOException { RequestCallback callback = Mockito.mock(RequestCallback.class); CseClientHttpRequest request = new CseClientHttpRequest(); CseRequestCallback cb = new CseRequestCallback(null, callback, null); cb.doWithRequest(request); Assertions.assertNull(request.getContext()); } @Test public void testCseEntity() throws IOException { CseHttpEntity entity = Mockito.mock(CseHttpEntity.class); RequestCallback callback = Mockito.mock(RequestCallback.class); CseClientHttpRequest request = new CseClientHttpRequest(); entity.addContext("c1", "c2"); CseRequestCallback cb = new CseRequestCallback(entity, callback, null); cb.doWithRequest(request); Assertions.assertEquals(entity.getContext(), request.getContext()); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRestTemplate.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCseRestTemplate { @Test public void testCseRestTemplate() { Assertions.assertEquals(CseRestTemplate.class, new CseRestTemplate().getClass()); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCseUriTemplateHandler { @Test public void testCrossApp() { CseUriTemplateHandler handler = new CseUriTemplateHandler(); URI uri = handler.expand("cse://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); Assertions.assertEquals("cse://app~ms/path?q=query", uri.toString()); Map vars = new HashMap<>(); vars.put("app", "app"); vars.put("path", "path"); vars.put("q", "query"); uri = handler.expand("cse://{app}:ms/{path}?q={q}", vars); Assertions.assertEquals("cse://app~ms/path?q=query", uri.toString()); uri = handler.expand("cse://ms/{path}?q={query}", "path", "query"); Assertions.assertEquals("cse://ms/path?q=query", uri.toString()); } @Test public void testCrossAppServiceComb() { CseUriTemplateHandler handler = new CseUriTemplateHandler(); URI uri = handler.expand("servicecomb://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); Assertions.assertEquals("servicecomb://app~ms/path?q=query", uri.toString()); Map vars = new HashMap<>(); vars.put("app", "app"); vars.put("path", "path"); vars.put("q", "query"); uri = handler.expand("servicecomb://{app}:ms/{path}?q={q}", vars); Assertions.assertEquals("servicecomb://app~ms/path?q=query", uri.toString()); uri = handler.expand("servicecomb://ms/{path}?q={query}", "path", "query"); Assertions.assertEquals("servicecomb://ms/path?q=query", uri.toString()); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRequestMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestRequestMeta { ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); Map pathParams = new HashMap<>(); RequestMeta requestmeta = new RequestMeta(referenceConfig, swaggerRestOperation, pathParams); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); @Test public void testGetReferenceConfig() { ReferenceConfig value = requestmeta.getReferenceConfig(); Assertions.assertNotNull(value); } @Test public void testGetPathParams() { Map value = requestmeta.getPathParams(); Assertions.assertNotNull(value); } @Test public void testGetSwaggerRestOperation() { RestOperationMeta value = requestmeta.getSwaggerRestOperation(); Assertions.assertNotNull(value); } @Test public void testGetOperationMeta() { Assertions.assertNull(requestmeta.getOperationMeta()); } @Test public void testGetOperationQualifiedName() { Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("value"); String qualifiedName = operationMeta.getSchemaQualifiedName(); Assertions.assertEquals("value", qualifiedName); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import java.net.URI; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; import org.springframework.web.client.RestOperations; import com.seanyinx.github.unit.scaffolding.Randomness; public class TestRestTemplateBuilder { private final String url = Randomness.uniquify("url"); private final AcceptableRestTemplate underlying = new AlwaysAcceptableRestTemplate(); private static class AlwaysAcceptableRestTemplate extends AcceptableRestTemplate { @Override public boolean isAcceptable(String uri) { return true; } @Override public boolean isAcceptable(URI uri) { return true; } } @Test public void addsRestTemplateToWrapper() { RestTemplateBuilder.addAcceptableRestTemplate(1, underlying); RestOperations restTemplate = RestTemplateBuilder.create(); MatcherAssert.assertThat(restTemplate, instanceOf(RestTemplateWrapper.class)); RestTemplateWrapper wrapper = (RestTemplateWrapper) restTemplate; MatcherAssert.assertThat(wrapper.getRestTemplate(url), is(underlying)); MatcherAssert.assertThat(wrapper.getRestTemplate(URI.create(url)), is(underlying)); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.http.HttpMethod.DELETE; import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.HEAD; import static org.springframework.http.HttpMethod.OPTIONS; import static org.springframework.http.HttpMethod.PATCH; import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpMethod.PUT; import static org.springframework.http.HttpMethod.TRACE; import static org.springframework.http.HttpStatus.OK; import java.net.URI; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriTemplateHandler; public class TestRestTemplateWrapper { private final AcceptableRestTemplate underlying = mock(AcceptableRestTemplate.class); private final RestTemplateWrapper wrapper = new RestTemplateWrapper(); private final String url = uniquify("someUrl"); private final URI uri = URI.create(url); private final String param1 = uniquify("param1"); private final String param2 = uniquify("param2"); private final Map paramsMap = new HashMap<>() { { put(uniquify("key1"), param1); put(uniquify("key2"), param2); } }; private final HttpEntity requestEntity = new HttpEntity<>(uniquify("requestBody")); private final String response = uniquify("response"); private final ResponseEntity> typedResponse = new ResponseEntity<>(singletonList(response), OK); private final ResponseEntity responseEntity = new ResponseEntity<>(response, OK); private final List httpMethods = asList(GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE); @BeforeEach public void setUp() throws Exception { when(underlying.isAcceptable(url)).thenReturn(true); when(underlying.isAcceptable(uri)).thenReturn(true); wrapper.addAcceptableRestTemplate(1, underlying); } @Test public void headForHeadersWithUnderlyingRestTemplate() { HttpHeaders expected = new HttpHeaders(); HttpHeaders actual; when(underlying.headForHeaders(url, param1, param2)).thenReturn(expected); actual = wrapper.headForHeaders(url, param1, param2); MatcherAssert.assertThat(actual, is(expected)); verify(underlying).headForHeaders(url, param1, param2); when(underlying.headForHeaders(url, paramsMap)).thenReturn(expected); actual = wrapper.headForHeaders(url, paramsMap); MatcherAssert.assertThat(actual, is(expected)); verify(underlying).headForHeaders(url, paramsMap); when(underlying.headForHeaders(uri)).thenReturn(expected); actual = wrapper.headForHeaders(uri); MatcherAssert.assertThat(actual, is(expected)); verify(underlying).headForHeaders(uri); } @Test public void optionsForAllowWithUnderlyingRestTemplate() { Set expected = new HashSet<>(this.httpMethods); Set actual; when(underlying.optionsForAllow(url, param1, param2)).thenReturn(expected); actual = wrapper.optionsForAllow(url, param1, param2); MatcherAssert.assertThat(actual, is(expected)); verify(underlying).optionsForAllow(url, param1, param2); when(underlying.optionsForAllow(url, paramsMap)).thenReturn(expected); actual = wrapper.optionsForAllow(url, paramsMap); MatcherAssert.assertThat(actual, is(expected)); verify(underlying).optionsForAllow(url, paramsMap); when(underlying.optionsForAllow(uri)).thenReturn(expected); actual = wrapper.optionsForAllow(uri); MatcherAssert.assertThat(actual, is(expected)); verify(underlying).optionsForAllow(uri); } @Test public void getForObjectWithUnderlyingRestTemplate() { String actual; when(underlying.getForObject(url, String.class, param1, param2)).thenReturn(response); actual = wrapper.getForObject(url, String.class, param1, param2); MatcherAssert.assertThat(actual, is(response)); verify(underlying).getForObject(url, String.class, param1, param2); when(underlying.getForObject(url, String.class, paramsMap)).thenReturn(response); actual = wrapper.getForObject(url, String.class, paramsMap); MatcherAssert.assertThat(actual, is(response)); verify(underlying).getForObject(url, String.class, paramsMap); when(underlying.getForObject(uri, String.class)).thenReturn(response); actual = wrapper.getForObject(uri, String.class); MatcherAssert.assertThat(actual, is(response)); verify(underlying).getForObject(uri, String.class); } @Test public void getForEntityWithUnderlyingRestTemplate() { ResponseEntity actual; when(underlying.getForEntity(url, String.class, param1, param2)).thenReturn(responseEntity); actual = wrapper.getForEntity(url, String.class, param1, param2); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).getForEntity(url, String.class, param1, param2); when(underlying.getForEntity(url, String.class, paramsMap)).thenReturn(responseEntity); actual = wrapper.getForEntity(url, String.class, paramsMap); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).getForEntity(url, String.class, paramsMap); when(underlying.getForEntity(uri, String.class)).thenReturn(responseEntity); actual = wrapper.getForEntity(uri, String.class); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).getForEntity(uri, String.class); } @Test public void postForObjectWithUnderlyingRestTemplate() { String actual; when(underlying.postForObject(url, requestEntity, String.class, param1, param2)).thenReturn(response); actual = wrapper.postForObject(url, requestEntity, String.class, param1, param2); MatcherAssert.assertThat(actual, is(response)); verify(underlying).postForObject(url, requestEntity, String.class, param1, param2); when(underlying.postForObject(url, requestEntity, String.class, paramsMap)).thenReturn(response); actual = wrapper.postForObject(url, requestEntity, String.class, paramsMap); MatcherAssert.assertThat(actual, is(response)); verify(underlying).postForObject(url, requestEntity, String.class, paramsMap); when(underlying.postForObject(uri, requestEntity, String.class)).thenReturn(response); actual = wrapper.postForObject(uri, requestEntity, String.class); MatcherAssert.assertThat(actual, is(response)); verify(underlying).postForObject(uri, requestEntity, String.class); } @Test public void postForEntityWithUnderlyingRestTemplate() { ResponseEntity actual; when(underlying.postForEntity(url, requestEntity, String.class, param1, param2)).thenReturn(responseEntity); actual = wrapper.postForEntity(url, requestEntity, String.class, param1, param2); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).postForEntity(url, requestEntity, String.class, param1, param2); when(underlying.postForEntity(url, requestEntity, String.class, paramsMap)).thenReturn(responseEntity); actual = wrapper.postForEntity(url, requestEntity, String.class, paramsMap); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).postForEntity(url, requestEntity, String.class, paramsMap); when(underlying.postForEntity(uri, requestEntity, String.class)).thenReturn(responseEntity); actual = wrapper.postForEntity(uri, requestEntity, String.class); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).postForEntity(uri, requestEntity, String.class); } @Test public void postForLocationWithUnderlyingRestTemplate() { URI actual; when(underlying.postForLocation(url, requestEntity, param1, param2)).thenReturn(uri); actual = wrapper.postForLocation(url, requestEntity, param1, param2); MatcherAssert.assertThat(actual, is(uri)); verify(underlying).postForLocation(url, requestEntity, param1, param2); when(underlying.postForLocation(url, requestEntity, paramsMap)).thenReturn(uri); actual = wrapper.postForLocation(url, requestEntity, paramsMap); MatcherAssert.assertThat(actual, is(uri)); verify(underlying).postForLocation(url, requestEntity, paramsMap); when(underlying.postForLocation(uri, requestEntity)).thenReturn(uri); actual = wrapper.postForLocation(uri, requestEntity); MatcherAssert.assertThat(actual, is(uri)); verify(underlying).postForLocation(uri, requestEntity); } @Test public void executeWithUnderlyingRestTemplate() { RequestCallback requestCallback = clientHttpRequest -> { }; ResponseExtractor> responseExtractor = clientHttpResponse -> responseEntity; ResponseEntity actual; for (HttpMethod method : httpMethods) { when(underlying.execute(url, method, requestCallback, responseExtractor, param1, param2)) .thenReturn(responseEntity); actual = wrapper.execute(url, method, requestCallback, responseExtractor, param1, param2); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).execute(url, method, requestCallback, responseExtractor, param1, param2); when(underlying.execute(url, method, requestCallback, responseExtractor, paramsMap)).thenReturn(responseEntity); actual = wrapper.execute(url, method, requestCallback, responseExtractor, paramsMap); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).execute(url, method, requestCallback, responseExtractor, paramsMap); when(underlying.execute(uri, method, requestCallback, responseExtractor)).thenReturn(responseEntity); actual = wrapper.execute(uri, method, requestCallback, responseExtractor); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).execute(uri, method, requestCallback, responseExtractor); } } @Test public void exchangeWithUnderlyingRestTemplate() { ResponseEntity actual; for (HttpMethod method : httpMethods) { when(underlying.exchange(url, method, requestEntity, String.class, param1, param2)).thenReturn(responseEntity); actual = wrapper.exchange(url, method, requestEntity, String.class, param1, param2); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(url, method, requestEntity, String.class, param1, param2); when(underlying.exchange(url, method, requestEntity, String.class, paramsMap)).thenReturn(responseEntity); actual = wrapper.exchange(url, method, requestEntity, String.class, paramsMap); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(url, method, requestEntity, String.class, paramsMap); when(underlying.exchange(uri, method, requestEntity, String.class)).thenReturn(responseEntity); actual = wrapper.exchange(uri, method, requestEntity, String.class); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(uri, method, requestEntity, String.class); RequestEntity request = new RequestEntity<>(method, uri); when(underlying.exchange(request, String.class)).thenReturn(responseEntity); actual = wrapper.exchange(request, String.class); MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(request, String.class); } } @Test public void exchangeUsingParameterizedTypeWithUnderlyingRestTemplate() { ParameterizedTypeReference> typeReference = new ParameterizedTypeReference<>() { }; ResponseEntity> actual; for (HttpMethod method : httpMethods) { when(underlying.exchange(url, method, requestEntity, typeReference, param1, param2)).thenReturn(typedResponse); actual = wrapper.exchange(url, method, requestEntity, typeReference, param1, param2); MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(url, method, requestEntity, typeReference, param1, param2); when(underlying.exchange(url, method, requestEntity, typeReference, paramsMap)).thenReturn(typedResponse); actual = wrapper.exchange(url, method, requestEntity, typeReference, paramsMap); MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(url, method, requestEntity, typeReference, paramsMap); when(underlying.exchange(uri, method, requestEntity, typeReference)).thenReturn(typedResponse); actual = wrapper.exchange(uri, method, requestEntity, typeReference); MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(uri, method, requestEntity, typeReference); RequestEntity request = new RequestEntity<>(method, uri); when(underlying.exchange(request, typeReference)).thenReturn(typedResponse); actual = wrapper.exchange(request, typeReference); MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(request, typeReference); } } @Test public void putWithUnderlyingRestTemplate() { wrapper.put(url, requestEntity, param1, param2); verify(underlying).put(url, requestEntity, param1, param2); wrapper.put(url, requestEntity, paramsMap); verify(underlying).put(url, requestEntity, paramsMap); wrapper.put(uri, requestEntity); verify(underlying).put(uri, requestEntity); } @Test public void deleteWithUnderlyingRestTemplate() { wrapper.delete(url, param1, param2); verify(underlying).delete(url, param1, param2); wrapper.delete(url, paramsMap); verify(underlying).delete(url, paramsMap); wrapper.delete(uri); verify(underlying).delete(uri); } @Test public void setInterceptorsWithUnderlying() { ClientHttpRequestInterceptor interceptor1 = mock(ClientHttpRequestInterceptor.class); ClientHttpRequestInterceptor interceptor2 = mock(ClientHttpRequestInterceptor.class); List interceptors = asList(interceptor1, interceptor2); wrapper.setInterceptors(interceptors); MatcherAssert.assertThat(wrapper.getInterceptors(), contains(interceptor1, interceptor2)); MatcherAssert.assertThat(wrapper.defaultRestTemplate.getInterceptors(), contains(interceptor1, interceptor2)); verify(underlying, never()).setInterceptors(interceptors); } @Test public void doNotSetRequestFactoryWithUnderlying() { ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class); wrapper.setRequestFactory(requestFactory); MatcherAssert.assertThat(wrapper.getRequestFactory(), is(requestFactory)); MatcherAssert.assertThat(wrapper.defaultRestTemplate.getRequestFactory(), is(requestFactory)); verify(underlying, never()).setRequestFactory(requestFactory); } @Test public void setErrorHandlerWithUnderlying() { ResponseErrorHandler errorHandler = mock(ResponseErrorHandler.class); wrapper.setErrorHandler(errorHandler); MatcherAssert.assertThat(wrapper.getErrorHandler(), is(errorHandler)); MatcherAssert.assertThat(wrapper.defaultRestTemplate.getErrorHandler(), is(errorHandler)); verify(underlying).setErrorHandler(errorHandler); } @Test public void setDefaultUriVariablesWithUnderlying() { Map uriVariables = new HashMap<>(); wrapper.setDefaultUriVariables(uriVariables); MatcherAssert.assertThat(defaultUriVariablesOf(wrapper), is(uriVariables)); MatcherAssert.assertThat(defaultUriVariablesOf(wrapper.defaultRestTemplate), is(uriVariables)); verify(underlying).setDefaultUriVariables(uriVariables); } @Test public void dotNotSetUriTemplateHandlerWithUnderlying() { UriTemplateHandler uriTemplateHandler = mock(UriTemplateHandler.class); wrapper.setUriTemplateHandler(uriTemplateHandler); MatcherAssert.assertThat(wrapper.getUriTemplateHandler(), is(uriTemplateHandler)); MatcherAssert.assertThat(wrapper.defaultRestTemplate.getUriTemplateHandler(), is(uriTemplateHandler)); verify(underlying, never()).setUriTemplateHandler(uriTemplateHandler); } @Test public void setMessageConvertersWithUnderlying() { ByteArrayHttpMessageConverter messageConverter = mock(ByteArrayHttpMessageConverter.class); wrapper.setMessageConverters(singletonList(messageConverter)); MatcherAssert.assertThat(wrapper.getMessageConverters(), contains(messageConverter)); MatcherAssert.assertThat(wrapper.defaultRestTemplate.getMessageConverters(), contains(messageConverter)); verify(underlying, never()).setMessageConverters(singletonList(messageConverter)); } @Test public void getsAcceptableRestTemplate() { MatcherAssert.assertThat(wrapper.getRestTemplate(uri), is(underlying)); MatcherAssert.assertThat(wrapper.getRestTemplate(url), is(underlying)); } @Test public void getsDefaultRestTemplate() { reset(underlying); MatcherAssert.assertThat(wrapper.getRestTemplate(uri), is(wrapper.defaultRestTemplate)); MatcherAssert.assertThat(wrapper.getRestTemplate(url), is(wrapper.defaultRestTemplate)); } private Map defaultUriVariablesOf(RestTemplate wrapper1) { return ((DefaultUriBuilderFactory) wrapper1.getUriTemplateHandler()).getDefaultUriVariables(); } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.provider.springmvc.reference.UrlWithProviderPrefixClientHttpRequestFactory.UrlWithProviderPrefixClientHttpRequest; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.http.HttpMethod; public class TestUrlWithProviderPrefixClientHttpRequestFactory { UrlWithProviderPrefixClientHttpRequestFactory factory = new UrlWithProviderPrefixClientHttpRequestFactory("/a/b/c"); URI uri = URI.create("cse://ms/a/b/c/v1/path"); @Test public void findUriPath() throws IOException { UrlWithProviderPrefixClientHttpRequest request = (UrlWithProviderPrefixClientHttpRequest) factory.createRequest(uri, HttpMethod.GET); Assertions.assertEquals("/v1/path", request.findUriPath(uri)); } @Test @SuppressWarnings("unchecked") public void invoke_checkPath() { Invocation invocation = Mockito.mock(Invocation.class); RequestMeta requestMeta = Mockito.mock(RequestMeta.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(requestMeta.getOperationMeta()).thenReturn(operationMeta); Map handlerContext = new HashMap<>(); UrlWithProviderPrefixClientHttpRequest request = new UrlWithProviderPrefixClientHttpRequest(uri, HttpMethod.GET, "/a/b/c") { @Override protected Response doInvoke(Invocation invocation) { return Response.ok(null); } }; Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); try (MockedStatic invocationFactoryMockedStatic = Mockito.mockStatic(InvocationFactory.class)) { invocationFactoryMockedStatic.when(() -> InvocationFactory.forConsumer(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(invocation); request.setRequestMeta(requestMeta); request.setPath(request.findUriPath(uri)); request.invoke(new HashMap<>()); Assertions.assertEquals("/v1/path", handlerContext.get(RestConst.REST_CLIENT_REQUEST_PATH)); } } } ================================================ FILE: providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithServiceNameClientHttpRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.provider.springmvc.reference; import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory.UrlWithServiceNameClientHttpRequest; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.http.HttpMethod; public class TestUrlWithServiceNameClientHttpRequestFactory { UrlWithServiceNameClientHttpRequestFactory factory = new UrlWithServiceNameClientHttpRequestFactory(); URI uri = URI.create("cse://ms/v1/path"); @Test public void findUriPath() throws IOException { UrlWithServiceNameClientHttpRequest request = (UrlWithServiceNameClientHttpRequest) factory.createRequest(uri, HttpMethod.GET); Assertions.assertEquals("/ms/v1/path", request.findUriPath(uri)); } @Test @SuppressWarnings("unchecked") public void invoke_checkPath() { Invocation invocation = Mockito.mock(Invocation.class); RequestMeta requestMeta = Mockito.mock(RequestMeta.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(requestMeta.getOperationMeta()).thenReturn(operationMeta); Map handlerContext = new HashMap<>(); UrlWithServiceNameClientHttpRequest request = new UrlWithServiceNameClientHttpRequest(uri, HttpMethod.GET) { @Override protected Response doInvoke(Invocation invocation) { return Response.ok(null); } }; Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); try (MockedStatic invocationFactoryMockedStatic = Mockito.mockStatic(InvocationFactory.class)) { invocationFactoryMockedStatic.when(() -> InvocationFactory.forConsumer(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(invocation); request.setRequestMeta(requestMeta); request.setPath(request.findUriPath(uri)); request.invoke(new HashMap<>()); Assertions.assertEquals("/ms/v1/path", handlerContext.get(RestConst.REST_CLIENT_REQUEST_PATH)); } } } ================================================ FILE: providers/provider-springmvc/src/test/resources/log4j2.xml ================================================ ================================================ FILE: providers/provider-springmvc/src/test/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb: filter-chains: consumer: default: empty producer: default: empty ================================================ FILE: samples/README.md ================================================ java-chassis samples are moved to [servicecomb-samples](https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples) ================================================ FILE: service-registry/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 service-registry-parent Java Chassis::Service Registry pom registry-local registry-service-center registry-lightweight registry-zero-config registry-nacos registry-zookeeper registry-etcd registry-consul ================================================ FILE: service-registry/registry-consul/pom.xml ================================================ 4.0.0 org.apache.servicecomb service-registry-parent 3.4.0-SNAPSHOT registry-consul Java Chassis::Service Registry::Consul org.kiwiproject consul-client org.apache.servicecomb foundation-common org.apache.servicecomb foundation-registry org.apache.servicecomb java-chassis-core com.google.code.gson gson ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; import com.google.common.net.HostAndPort; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; import org.apache.servicecomb.registry.consul.config.ConsulProperties; import org.kiwiproject.consul.Consul; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @ConditionalOnProperty(prefix = "servicecomb.registry.consul", name = "enabled", matchIfMissing = true) @Configuration public class ConsulConfiguration { @Bean @ConfigurationProperties(prefix = ConsulConst.CONSUL_REGISTRY_PREFIX) @ConditionalOnMissingBean public ConsulProperties consulProperties() { return new ConsulProperties(); } @Bean @ConfigurationProperties(prefix = ConsulConst.CONSUL_DISCOVERY_REGISTRY_PREFIX) @ConditionalOnMissingBean public ConsulDiscoveryProperties consulDiscoveryProperties() { return new ConsulDiscoveryProperties(); } @Bean @ConditionalOnBean(value = {ConsulProperties.class, ConsulDiscoveryProperties.class}) @ConditionalOnMissingBean public Consul consulClient(ConsulProperties consulProperties, ConsulDiscoveryProperties consulDiscoveryProperties) { Consul.Builder builder = Consul.builder() .withHostAndPort(HostAndPort.fromParts(consulProperties.getHost(), consulProperties.getPort())); if (StringUtils.isNotBlank(consulDiscoveryProperties.getAclToken())) { builder.withAclToken(consulDiscoveryProperties.getAclToken()); } return builder.build(); } @Bean @ConditionalOnBean(value = {Consul.class, Environment.class, RegistrationId.class, DataCenterProperties.class}) @ConditionalOnMissingBean public ConsulDiscovery consulDiscovery() { return new ConsulDiscovery(); } @Bean @ConditionalOnBean(value = {Consul.class, Environment.class, RegistrationId.class, DataCenterProperties.class}) @ConditionalOnMissingBean public ConsulRegistration consulRegistration() { return new ConsulRegistration(); } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; public class ConsulConst { public static final String CONSUL_REGISTRY_NAME = "consul-registry"; public static final String CONSUL_REGISTRY_PREFIX = "servicecomb.registry.consul"; public static final String CONSUL_DISCOVERY_REGISTRY_PREFIX = "servicecomb.registry.consul.discovery"; public static final String CONSUL_DEFAULT_ENVIRONMENT = "production"; } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import jakarta.annotation.Resource; import jakarta.validation.constraints.NotNull; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; import org.apache.servicecomb.registry.consul.config.ConsulProperties; import org.kiwiproject.consul.Consul; import org.kiwiproject.consul.HealthClient; import org.kiwiproject.consul.cache.ServiceHealthCache; import org.kiwiproject.consul.cache.ServiceHealthKey; import org.kiwiproject.consul.model.health.Service; import org.kiwiproject.consul.model.health.ServiceHealth; import org.kiwiproject.consul.option.Options; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Map; public class ConsulDiscovery implements Discovery { private static final Logger LOGGER = LoggerFactory.getLogger(ConsulDiscovery.class); @Resource private ConsulProperties consulProperties; @Resource private ConsulDiscoveryProperties consulDiscoveryProperties; @Resource private Consul consulClient; @Resource private Environment environment; private List consulDiscoveryInstanceList; private InstanceChangedListener instanceChangedListener; private ServiceHealthCache svHealth; @Override public String name() { return ConsulConst.CONSUL_REGISTRY_NAME; } @Override public boolean enabled(String application, String serviceName) { return consulDiscoveryProperties.isEnabled(); } @Override public List findServiceInstances(String application, String serviceName) { LOGGER.info("findServiceInstances application:{}, serviceName:{}", application, serviceName); consulDiscoveryInstanceList = getInstances(serviceName); return consulDiscoveryInstanceList; } @Override public List findServices(String application) { LOGGER.info("ConsulDiscovery findServices(application={})", application); Map> response = consulClient.catalogClient().getServices().getResponse(); if (!CollectionUtils.isEmpty(response)) { return Lists.newArrayList(response.keySet()); } return Lists.newArrayList(); } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { this.instanceChangedListener = instanceChangedListener; } @Override public boolean enabled() { return consulDiscoveryProperties.isEnabled(); } @Override public void init() { LOGGER.info("ConsulDiscovery init"); } @Override public void run() { LOGGER.info("ConsulDiscovery run"); String serviceName = BootStrapProperties.readServiceName(environment); HealthClient healthClient = consulClient.healthClient(); svHealth = ServiceHealthCache.newCache(healthClient, serviceName, true, Options.BLANK_QUERY_OPTIONS, consulDiscoveryProperties.getWatchSeconds()); svHealth.addListener((Map newValues) -> instanceChangedListener.onInstanceChanged( name(), BootStrapProperties.readApplication(environment), serviceName, getInstances(serviceName))); svHealth.start(); } @Override public void destroy() { if (svHealth != null) { svHealth.stop(); } String serviceId = consulDiscoveryProperties.getServiceId(); LOGGER.info("ConsulDiscovery destroy consul service id={}", serviceId); if (consulClient != null) { LOGGER.info("ConsulDiscovery consulClient destroy"); consulClient.agentClient().deregister(serviceId); consulClient.destroy(); } } public List getInstances(@NotNull final String serviceName) { List instances = new ArrayList<>(); addInstancesToList(instances, serviceName); return instances; } private void addInstancesToList(List instances, String serviceName) { List healthServices = getHealthServices(serviceName); if (!CollectionUtils.isEmpty(healthServices)) { for (ServiceHealth serviceHealth : healthServices) { Service service = serviceHealth.getService(); Gson gson = new GsonBuilder().disableHtmlEscaping().create(); ConsulInstance consulInstance = gson.fromJson(service.getMeta().get("meta"), ConsulInstance.class); instances.add(new ConsulDiscoveryInstance(consulInstance)); } } } private List getHealthServices(String serviceName) { HealthClient healthClient = consulClient.healthClient(); return healthClient.getHealthyServiceInstances(serviceName).getResponse(); } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; import org.apache.servicecomb.registry.api.DiscoveryInstance; public class ConsulDiscoveryInstance extends ConsulInstance implements DiscoveryInstance { public ConsulDiscoveryInstance(ConsulInstance consulInstance) { super(consulInstance); } @Override public String getRegistryName() { return ConsulConst.CONSUL_REGISTRY_NAME; } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstance; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ConsulInstance implements MicroserviceInstance { private String serviceId; private String instanceId; private String environment; private String application; private String serviceName; private String alias; private String version; private String description; private DataCenterInfo dataCenterInfo; private List endpoints = new ArrayList<>(); private Map schemas = new HashMap<>(); private Map properties = new HashMap<>(); private MicroserviceInstanceStatus status; public ConsulInstance() { } public ConsulInstance(ConsulInstance other) { this.serviceId = other.serviceId; this.instanceId = other.instanceId; this.environment = other.environment; this.application = other.application; this.serviceName = other.serviceName; this.alias = other.alias; this.version = other.version; this.description = other.description; this.dataCenterInfo = other.dataCenterInfo; this.endpoints = other.endpoints; this.schemas = other.schemas; this.properties = other.properties; this.status = other.status; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public void setEnvironment(String environment) { this.environment = environment; } public void setApplication(String application) { this.application = application; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public void setAlias(String alias) { this.alias = alias; } public void setVersion(String version) { this.version = version; } public void setDescription(String description) { this.description = description; } public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { this.dataCenterInfo = dataCenterInfo; } public void setEndpoints(List endpoints) { this.endpoints = endpoints; } public void setSchemas(Map schemas) { this.schemas = schemas; } public void setProperties(Map properties) { this.properties = properties; } public void setStatus(MicroserviceInstanceStatus status) { this.status = status; } @Override public String getEnvironment() { return this.environment; } @Override public String getApplication() { return this.application; } @Override public String getServiceName() { return this.serviceName; } @Override public String getAlias() { return alias; } @Override public String getVersion() { return version; } @Override public DataCenterInfo getDataCenterInfo() { return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; } @Override public String getDescription() { return description; } @Override public Map getProperties() { return properties; } @Override public Map getSchemas() { return schemas; } @Override public List getEndpoints() { return endpoints; } public void addSchema(String schemaId, String content) { this.schemas.put(schemaId, content); } public void addEndpoint(String endpoint) { this.endpoints.add(endpoint); } public void addProperty(String key, String value) { this.properties.put(key, value); } @Override public String getInstanceId() { return instanceId; } @Override public String getServiceId() { return serviceId; } @Override public MicroserviceInstanceStatus getStatus() { return this.status; } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; import org.kiwiproject.consul.AgentClient; import org.kiwiproject.consul.Consul; import org.kiwiproject.consul.model.agent.ImmutableRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import java.util.HashMap; import java.util.List; import java.util.Map; public class ConsulRegistration implements Registration { private static final Logger LOGGER = LoggerFactory.getLogger(ConsulRegistration.class); private ConsulInstance consulInstance; @Resource private ConsulDiscoveryProperties consulDiscoveryProperties; @Resource private Consul consulClient; @Resource private Environment environment; @Resource private DataCenterProperties dataCenterProperties; @Resource private RegistrationId registrationId; @Value("${servicecomb.rest.address:127.0.0.1:8080}") private String restAddress; private ImmutableRegistration.Builder registrationBuilder; @Override public String name() { return ConsulConst.CONSUL_REGISTRY_NAME; } @Override public ConsulRegistrationInstance getMicroserviceInstance() { return new ConsulRegistrationInstance(consulInstance); } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { consulInstance.setStatus(status); Gson gson = new GsonBuilder().disableHtmlEscaping().create(); Map meta = new HashMap<>(); meta.put("meta", gson.toJson(consulInstance)); registrationBuilder.meta(meta); ImmutableRegistration newService = registrationBuilder.build(); AgentClient agentClient = consulClient.agentClient(); agentClient.register(newService); return true; } @Override public void addSchema(String schemaId, String content) { if (consulDiscoveryProperties.isEnableSwaggerRegistration()) { consulInstance.addSchema(schemaId, content); } } @Override public void addEndpoint(String endpoint) { consulInstance.addEndpoint(endpoint); } @Override public void addProperty(String key, String value) { consulInstance.addProperty(key, value); } @Override public boolean enabled() { return consulDiscoveryProperties.isEnabled(); } @Override public void init() { LOGGER.info("ConsulRegistration init"); String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = ConsulConst.CONSUL_DEFAULT_ENVIRONMENT; } String instanceId = registrationId.getInstanceId(); consulInstance = new ConsulInstance(); consulInstance.setInstanceId(instanceId); consulInstance.setEnvironment(env); consulInstance.setApplication(BootStrapProperties.readApplication(environment)); consulInstance.setServiceName(BootStrapProperties.readServiceName(environment)); consulInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); consulInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setName(dataCenterProperties.getName()); dataCenterInfo.setRegion(dataCenterProperties.getRegion()); dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); consulInstance.setDataCenterInfo(dataCenterInfo); } consulInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); consulInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); consulInstance.setStatus( MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); } @Override public void run() { LOGGER.info("ConsulRegistration run"); registrationBuilder = ImmutableRegistration.builder() .name(consulInstance.getServiceName()); List endpoints = consulInstance.getEndpoints(); for (String endpoint : endpoints) { Endpoint temp = EndpointUtils.parse(endpoint); if (temp.getAddress() instanceof URIEndpointObject) { String hostOrIp = ((URIEndpointObject) temp.getAddress()).getHostOrIp(); int port = ((URIEndpointObject) temp.getAddress()).getPort(); String serviceId = hostOrIp + ":" + port; consulDiscoveryProperties.setServiceId(serviceId); consulInstance.setServiceId(serviceId); registrationBuilder.id(serviceId); registrationBuilder.address(hostOrIp); registrationBuilder.port(port); break; } } List tags = consulDiscoveryProperties.getTags(); if (CollectionUtils.isEmpty(tags)) { tags.add(consulInstance.getServiceName()); } registrationBuilder.tags(tags); Gson gson = new GsonBuilder().disableHtmlEscaping().create(); Map meta = new HashMap<>(); meta.put("meta", gson.toJson(consulInstance)); registrationBuilder.meta(meta); ImmutableRegistration newService = registrationBuilder.build(); AgentClient agentClient = consulClient.agentClient(); agentClient.register(newService); LOGGER.info("ConsulRegistration newService"); } @Override public void destroy() { LOGGER.info("ConsulRegistration destroy"); } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul; import org.apache.servicecomb.registry.api.RegistrationInstance; public class ConsulRegistrationInstance extends ConsulInstance implements RegistrationInstance { public ConsulRegistrationInstance(ConsulInstance instance) { super(instance); } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul.config; import org.springframework.core.style.ToStringCreator; import java.util.ArrayList; import java.util.List; public class ConsulDiscoveryProperties { private String aclToken; private List tags = new ArrayList<>(); private Boolean enabled = true; private Integer watchSeconds = 8; private Boolean enableSwaggerRegistration = false; private String serviceId; public String getAclToken() { return this.aclToken; } public void setAclToken(String aclToken) { this.aclToken = aclToken; } public List getTags() { return this.tags; } public void setTags(List tags) { this.tags = tags; } public Boolean isEnabled() { return this.enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean isEnableSwaggerRegistration() { return enableSwaggerRegistration; } public void setEnableSwaggerRegistration(Boolean enableSwaggerRegistration) { this.enableSwaggerRegistration = enableSwaggerRegistration; } public Integer getWatchSeconds() { return watchSeconds; } public void setWatchSeconds(Integer watchSeconds) { this.watchSeconds = watchSeconds; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } @Override public String toString() { return new ToStringCreator(this).append("aclToken", this.aclToken) .append("enabled", this.enabled) .append("serviceId", this.serviceId) .append("tags", this.tags) .append("watchSeconds", this.watchSeconds) .toString(); } } ================================================ FILE: service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.consul.config; import jakarta.validation.constraints.NotNull; import org.springframework.validation.annotation.Validated; @Validated public class ConsulProperties { @NotNull private String host = "localhost"; private String scheme = "http"; @NotNull private Integer port = 8500; private Boolean enabled = true; public String getHost() { return this.host; } public void setHost(String host) { this.host = host; } public Integer getPort() { return this.port; } public void setPort(Integer port) { this.port = port; } public Boolean isEnabled() { return this.enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public String getScheme() { return this.scheme; } public void setScheme(String scheme) { this.scheme = scheme; } @Override public String toString() { return "ConsulProperties{" + "host='" + this.host + '\'' + ", port=" + this.port + ", scheme=" + this.scheme + ", enabled=" + this.enabled + "}"; } } ================================================ FILE: service-registry/registry-consul/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.consul.ConsulConfiguration ================================================ FILE: service-registry/registry-etcd/pom.xml ================================================ org.apache.servicecomb service-registry-parent 3.4.0-SNAPSHOT 4.0.0 registry-etcd Java Chassis::Service Registry::Etcd io.etcd jetcd-core org.apache.servicecomb foundation-common org.apache.servicecomb foundation-registry org.apache.servicecomb java-chassis-core ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class EtcdConfiguration { @Bean @ConfigurationProperties(prefix = EtcdConst.ETCD_REGISTRY_PREFIX) public EtcdRegistryProperties etcdRegistryProperties() { return new EtcdRegistryProperties(); } @Bean public EtcdDiscovery etcdDiscovery() { return new EtcdDiscovery(); } @Bean public EtcdRegistration etcdRegistration() { return new EtcdRegistration(); } } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; public class EtcdConst { public static final String ETCD_REGISTRY_NAME = "etcd-registry"; public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/registry/%s"; public static final String ETCD_REGISTRY_PREFIX = "servicecomb.registry.etcd"; public static final String ETCD_DISCOVERY_ENABLED = ETCD_REGISTRY_PREFIX + ".%s.%s.enabled"; public static final String ETCD_DEFAULT_ENVIRONMENT = "production"; } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.ConditionWaiter; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; import org.apache.servicecomb.registry.api.Discovery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.collect.Lists; import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; import io.etcd.jetcd.KeyValue; import io.etcd.jetcd.Watch; import io.etcd.jetcd.kv.GetResponse; import io.etcd.jetcd.options.GetOption; import io.etcd.jetcd.options.WatchOption; public class EtcdDiscovery implements Discovery { private Environment environment; private String basePath; private EtcdRegistryProperties etcdRegistryProperties; private Client client; private InstanceChangedListener instanceChangedListener; private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); private Map watchMap = new ConcurrentHashMapEx<>(); @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired @SuppressWarnings("unused") public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { this.etcdRegistryProperties = etcdRegistryProperties; } @Override public String name() { return EtcdConst.ETCD_REGISTRY_NAME; } @Override public boolean enabled(String application, String serviceName) { return environment.getProperty(String.format(EtcdConst.ETCD_DISCOVERY_ENABLED, application, serviceName), boolean.class, true); } @Override public List findServiceInstances(String application, String serviceName) { String prefixPath = basePath + "/" + application + "/" + serviceName; watchMap.computeIfAbsent(prefixPath, listenServiceName -> { Watch watchClient = client.getWatchClient(); try { ByteSequence prefixByteSeq = ByteSequence.from(prefixPath, Charset.defaultCharset()); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> watchNode(application, serviceName, prefixPath)); } catch (Exception e) { LOGGER.error("Failed to add watch", e); } return watchClient; }); // async get all instances,because sync is bad way in etcd. ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, TimeUnit.MILLISECONDS); waiter.executeTaskAsync(() -> { CompletableFuture getFuture = client.getKVClient() .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); GetResponse getResponse = getFuture.get(); return convertServiceInstanceList(getResponse.getKvs()); }); return waiter.waitForCompletion(); } private void watchNode(String application, String serviceName, String prefixPath) { CompletableFuture getFuture = client.getKVClient() .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); getFuture.thenAcceptAsync(response -> { List discoveryInstanceList = convertServiceInstanceList(response.getKvs()); instanceChangedListener.onInstanceChanged(name(), application, serviceName, discoveryInstanceList); }).exceptionally(e -> { LOGGER.error("watchNode error", e); return null; }); } private List getValuesByPrefix(String prefix) { CompletableFuture getFuture = client.getKVClient() .get(ByteSequence.from(prefix, StandardCharsets.UTF_8), GetOption.builder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build()); GetResponse response = MuteExceptionUtil.builder().withLog("get kv by prefix error") .executeCompletableFuture(getFuture); return response.getKvs(); } private List convertServiceInstanceList(List keyValueList) { List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); for (KeyValue keyValue : keyValueList) { EtcdDiscoveryInstance etcdDiscoveryInstance = getEtcdDiscoveryInstance(keyValue); list.add(etcdDiscoveryInstance); } return list; } private static EtcdDiscoveryInstance getEtcdDiscoveryInstance(KeyValue keyValue) { String valueJson = new String(keyValue.getValue().getBytes(), Charset.defaultCharset()); EtcdInstance etcdInstance = MuteExceptionUtil.builder() .withLog("convert json value to obj from etcd failure, {}", valueJson) .executeFunctionWithDoubleParam(JsonUtils::readValue, valueJson.getBytes(StandardCharsets.UTF_8), EtcdInstance.class); return new EtcdDiscoveryInstance(etcdInstance); } @Override public List findServices(String application) { ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, TimeUnit.MILLISECONDS); waiter.executeTaskAsync(() -> { String prefixPath = basePath + "/" + application; List endpointKv = getValuesByPrefix(prefixPath); return endpointKv.stream() .map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) .map(key -> { String[] parts = StringUtils.split(key, "/"); return parts.length > 5 ? parts[4] : null; }) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); }); return waiter.waitForCompletion(); } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { this.instanceChangedListener = instanceChangedListener; } @Override public boolean enabled() { return etcdRegistryProperties.isEnabled(); } @Override public void init() { String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT; } basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); } @Override public void run() { if (StringUtils.isEmpty(etcdRegistryProperties.getAuthenticationInfo())) { this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build(); } else { String[] authInfo = etcdRegistryProperties.getAuthenticationInfo().split(":"); this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) .user(ByteSequence.from(authInfo[0], Charset.defaultCharset())) .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())).build(); } } @Override public void destroy() { if (client != null) { client.close(); watchMap = null; } } } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; import org.apache.servicecomb.registry.api.DiscoveryInstance; public class EtcdDiscoveryInstance extends EtcdInstance implements DiscoveryInstance { public EtcdDiscoveryInstance(EtcdInstance other) { super(other); } @Override public String getRegistryName() { return EtcdConst.ETCD_REGISTRY_NAME; } } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstance; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; public class EtcdInstance implements MicroserviceInstance { private String serviceId; private String instanceId; private String environment; private String application; private String serviceName; private String alias; private String version; private String description; private DataCenterInfo dataCenterInfo; private List endpoints = new ArrayList<>(); private Map schemas = new HashMap<>(); private Map properties = new HashMap<>(); private MicroserviceInstanceStatus status; public EtcdInstance() { } public EtcdInstance(EtcdInstance other) { this.serviceId = other.serviceId; this.instanceId = other.instanceId; this.environment = other.environment; this.application = other.application; this.serviceName = other.serviceName; this.alias = other.alias; this.version = other.version; this.description = other.description; this.dataCenterInfo = other.dataCenterInfo; this.endpoints = other.endpoints; this.schemas = other.schemas; this.properties = other.properties; this.status = other.status; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public void setEnvironment(String environment) { this.environment = environment; } public void setApplication(String application) { this.application = application; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public void setAlias(String alias) { this.alias = alias; } public void setVersion(String version) { this.version = version; } public void setDescription(String description) { this.description = description; } public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { this.dataCenterInfo = dataCenterInfo; } public void setEndpoints(List endpoints) { this.endpoints = endpoints; } public void setSchemas(Map schemas) { this.schemas = schemas; } public void setProperties(Map properties) { this.properties = properties; } public void setStatus(MicroserviceInstanceStatus status) { this.status = status; } @Override public String getEnvironment() { return this.environment; } @Override public String getApplication() { return this.application; } @Override public String getServiceName() { return this.serviceName; } @Override public String getAlias() { return alias; } @Override public String getVersion() { return version; } @Override public DataCenterInfo getDataCenterInfo() { return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; } @Override public String getDescription() { return description; } @Override public Map getProperties() { return properties; } @Override public Map getSchemas() { return schemas; } @Override public List getEndpoints() { return endpoints; } public void addSchema(String schemaId, String content) { this.schemas.put(schemaId, content); } public void addEndpoint(String endpoint) { this.endpoints.add(endpoint); } public void addProperty(String key, String value) { this.properties.put(key, value); } @Override public String getInstanceId() { return instanceId; } @Override public String getServiceId() { return serviceId; } @Override public MicroserviceInstanceStatus getStatus() { return this.status; } @Override public String toString() { return "EtcdInstance{" + "serviceId='" + serviceId + '\'' + ", instanceId='" + instanceId + '\'' + ", environment='" + environment + '\'' + ", application='" + application + '\'' + ", serviceName='" + serviceName + '\'' + ", alias='" + alias + '\'' + ", version='" + version + '\'' + ", description='" + description + '\'' + ", dataCenterInfo=" + dataCenterInfo + ", endpoints=" + endpoints + ", schemas=" + schemas + ", properties=" + properties + '}'; } } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; import java.nio.charset.Charset; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; import io.etcd.jetcd.KV; import io.etcd.jetcd.Lease; import io.etcd.jetcd.kv.PutResponse; import io.etcd.jetcd.options.PutOption; public class EtcdRegistration implements Registration { private EtcdInstance etcdInstance; private Environment environment; private String basePath; private RegistrationId registrationId; private DataCenterProperties dataCenterProperties; private EtcdRegistryProperties etcdRegistryProperties; private Client client; private ScheduledExecutorService executorService; private String keyPath; private Long leaseId; @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired @SuppressWarnings("unused") public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { this.etcdRegistryProperties = etcdRegistryProperties; } @Autowired public void setRegistrationId(RegistrationId registrationId) { this.registrationId = registrationId; } @Autowired @SuppressWarnings("unused") public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { this.dataCenterProperties = dataCenterProperties; } @Override public String name() { return EtcdConst.ETCD_REGISTRY_NAME; } @Override public EtcdRegistrationInstance getMicroserviceInstance() { return new EtcdRegistrationInstance(etcdInstance); } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { this.etcdInstance.setStatus(status); String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{}, value:{}", keyPath, etcdInstance) .executeFunction(JsonUtils::writeValueAsString, etcdInstance); register(ByteSequence.from(keyPath, Charset.defaultCharset()), ByteSequence.from(valueJson, Charset.defaultCharset())); return true; } @Override public void addSchema(String schemaId, String content) { if (etcdRegistryProperties.isEnableSwaggerRegistration()) { etcdInstance.addSchema(schemaId, content); } } @Override public void addEndpoint(String endpoint) { etcdInstance.addEndpoint(endpoint); } @Override public void addProperty(String key, String value) { etcdInstance.addProperty(key, value); } @Override public boolean enabled() { return etcdRegistryProperties.isEnabled(); } @Override public void init() { String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT; } basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); etcdInstance = new EtcdInstance(); etcdInstance.setInstanceId(registrationId.getInstanceId()); etcdInstance.setEnvironment(env); etcdInstance.setApplication(BootStrapProperties.readApplication(environment)); etcdInstance.setServiceName(BootStrapProperties.readServiceName(environment)); etcdInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); etcdInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setName(dataCenterProperties.getName()); dataCenterInfo.setRegion(dataCenterProperties.getRegion()); dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); etcdInstance.setDataCenterInfo(dataCenterInfo); } etcdInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); etcdInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); etcdInstance.setStatus( MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); } @Override public void run() { client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) .build(); keyPath = basePath + "/" + BootStrapProperties.readApplication(environment) + "/" + BootStrapProperties.readServiceName(environment) + "/" + registrationId.getInstanceId(); String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{}, value:{}", keyPath, etcdInstance) .executeFunction(JsonUtils::writeValueAsString, etcdInstance); register(ByteSequence.from(keyPath, Charset.defaultCharset()), ByteSequence.from(valueJson, Charset.defaultCharset())); } public void register(ByteSequence key, ByteSequence value) { Lease leaseClient = client.getLeaseClient(); leaseId = MuteExceptionUtil.builder().withLog("get lease id, key:{}, value:{}", keyPath, etcdInstance) .executeCompletableFuture(leaseClient.grant(60)).getID(); KV kvClient = client.getKVClient(); PutOption putOption = PutOption.builder().withLeaseId(leaseId).build(); CompletableFuture putResponse = kvClient.put(key, value, putOption); putResponse.thenRun(() -> { executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleAtFixedRate( () -> MuteExceptionUtil.builder().withLog("reRegister, {}, {}", keyPath, etcdInstance) .executeFunction(leaseClient::keepAliveOnce, leaseId), 0, 5, TimeUnit.SECONDS); }); } private void unregister() { // close job executorService.shutdownNow(); // close etcd node Lease leaseClient = client.getLeaseClient(); leaseClient.revoke(leaseId); client.getKVClient().delete(ByteSequence.from(keyPath, Charset.defaultCharset())); } @Override public void destroy() { if (client != null) { unregister(); client.close(); } } } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; import org.apache.servicecomb.registry.api.RegistrationInstance; public class EtcdRegistrationInstance extends EtcdInstance implements RegistrationInstance { public EtcdRegistrationInstance(EtcdInstance instance) { super(instance); } } ================================================ FILE: service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.etcd; public class EtcdRegistryProperties { private boolean enabled = true; private boolean ephemeral = true; private String connectString = "http://127.0.0.1:2379"; private String authenticationSchema; private String authenticationInfo; private int connectionTimeoutMillis = 1000; private int sessionTimeoutMillis = 60000; private boolean enableSwaggerRegistration = false; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isEphemeral() { return ephemeral; } public void setEphemeral(boolean ephemeral) { this.ephemeral = ephemeral; } public String getConnectString() { return connectString; } public void setConnectString(String connectString) { this.connectString = connectString; } public int getConnectionTimeoutMillis() { return connectionTimeoutMillis; } public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { this.connectionTimeoutMillis = connectionTimeoutMillis; } public int getSessionTimeoutMillis() { return sessionTimeoutMillis; } public void setSessionTimeoutMillis(int sessionTimeoutMillis) { this.sessionTimeoutMillis = sessionTimeoutMillis; } public boolean isEnableSwaggerRegistration() { return enableSwaggerRegistration; } public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { this.enableSwaggerRegistration = enableSwaggerRegistration; } public String getAuthenticationSchema() { return authenticationSchema; } public void setAuthenticationSchema(String authenticationSchema) { this.authenticationSchema = authenticationSchema; } public String getAuthenticationInfo() { return authenticationInfo; } public void setAuthenticationInfo(String authenticationInfo) { this.authenticationInfo = authenticationInfo; } } ================================================ FILE: service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.etcd.EtcdConfiguration ================================================ FILE: service-registry/registry-lightweight/pom.xml ================================================ service-registry-parent org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 registry-lightweight Java Chassis::Service Registry::lightweight org.apache.servicecomb registry-local org.apache.servicecomb provider-jaxrs org.apache.servicecomb provider-pojo org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.lightweight.store.Store; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; public abstract class AbstractLightweightDiscovery implements Discovery { public static final String ZERO_DISCOVERY_ENABLED = "servicecomb.registry.zero-config.%s.%s.enabled"; protected EventBus eventBus; protected Store store; private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public AbstractLightweightDiscovery setEventBus(EventBus eventBus) { this.eventBus = eventBus; return this; } @Autowired public AbstractLightweightDiscovery setStore(Store store) { this.store = store; return this; } @Override public void init() { eventBus.register(this); } @Override public void destroy() { } @Override public boolean enabled(String application, String serviceName) { return environment.getProperty(String.format(ZERO_DISCOVERY_ENABLED, application, serviceName), boolean.class, true); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.io.IOException; import java.time.Duration; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.eventbus.EventBus; @SuppressWarnings("UnstableApiUsage") public abstract class AbstractLightweightRegistration implements Registration { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLightweightRegistration.class); protected EventBus eventBus; protected StoreService storeService; protected Self self; @Autowired public AbstractLightweightRegistration setEventBus(EventBus eventBus) { this.eventBus = eventBus; return this; } @Autowired public AbstractLightweightRegistration setStoreService(StoreService storeService) { this.storeService = storeService; return this; } @Autowired public AbstractLightweightRegistration setSelf(Self self) { this.self = self; return this; } @Override public void init() { } protected void startRegister(Duration interval) { Executors .newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, name())) .scheduleAtFixedRate(this::sendRegister, 0, interval.getSeconds(), TimeUnit.SECONDS); } protected void sendRegister() { try { doSendRegister(); } catch (Exception e) { LOGGER.error("register failed.", e); } } protected abstract void doSendRegister() throws IOException; @Override public void addSchema(String schemaId, String content) { self.addSchema(schemaId, content); } @Override public void addEndpoint(String endpoint) { self.getInstance().getEndpoints().add(endpoint); } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { return true; } @Override public void addProperty(String key, String value) { } @Override public void run() { storeService.registerSelf(self); } @Override public void destroy() { try { doSendUnregister(); } catch (Exception e) { LOGGER.error("unregister failed.", e); } } protected abstract void doSendUnregister() throws IOException; } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import io.swagger.v3.oas.annotations.Operation; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; @Path("/v1/discovery") public interface DiscoveryClient { @Path("/info") @GET @Operation(summary = "", operationId = "getInfo") CompletableFuture getInfoAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId); default MicroserviceInfo getInfo(Endpoint endpoint, String serviceId) { return AsyncUtils.toSync(getInfoAsync(endpoint, serviceId)); } @Path("/microservice") @GET @Operation(summary = "", operationId = "getMicroservice") CompletableFuture getMicroserviceAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId); default Microservice getMicroservice(Endpoint endpoint, String serviceId) { return AsyncUtils.toSync(getMicroserviceAsync(endpoint, serviceId)); } @Path("/instance") @GET @Operation(summary = "", operationId = "getInstance") CompletableFuture getInstanceAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId); default MicroserviceInstance getInstance(Endpoint endpoint, String serviceId) { return AsyncUtils.toSync(getInstanceAsync(endpoint, serviceId)); } @Path("/schemas/{schema-id}") @GET @Produces(MediaType.TEXT_PLAIN) @Operation(summary = "", operationId = "getSchema") CompletableFuture getSchemaAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId, @PathParam("schema-id") String schemaId); default String getSchema(Endpoint endpoint, String serviceId, String schemaId) { return AsyncUtils.toSync(getSchemaAsync(endpoint, serviceId, schemaId)); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import static org.apache.servicecomb.registry.lightweight.DiscoveryEndpoint.SCHEMA_ID; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; @RestSchema(schemaId = SCHEMA_ID) @Path("/v1/discovery") public class DiscoveryEndpoint { public static final String SCHEMA_ID = "scb-discovery"; private final Self self; public DiscoveryEndpoint(Self self) { this.self = self; } @Parameters( { @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), description = "just make it possible to mock many instances by one real instance for performance test") } ) @Path("/info") @GET public CompletableFuture getInfo() { return CompletableFuture.completedFuture(self.getMicroserviceInfo()); } @Parameters( { @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), description = "just make it possible to mock many instances by one real instance for performance test") } ) @Path("/microservice") @GET public CompletableFuture getMicroservice() { return CompletableFuture.completedFuture(self.getMicroservice()); } @Parameters( { @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), description = "just make it possible to mock many instances by one real instance for performance test") } ) @Path("/instance") @GET public CompletableFuture getInstance() { return CompletableFuture.completedFuture(self.getInstance()); } @Parameters( { @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), description = "just make it possible to mock many instances by one real instance for performance test") } ) @Path("/schemas/{schema-id}") @GET @Produces(MediaType.TEXT_PLAIN) public CompletableFuture getSchema(@PathParam("schema-id") String schemaId) { return CompletableFuture.completedFuture(self.getMicroservice().getSchemaMap().get(schemaId)); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.util.Collections; import java.util.List; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.localregistry.RegistryBean; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.apache.servicecomb.localregistry.RegistryBean.Instances; import org.apache.servicecomb.provider.pojo.Invoker; import org.apache.servicecomb.registry.lightweight.store.Store; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; @Configuration @SuppressWarnings("unused") public class LightWeightRegistryConfiguration { public static final String ZERO_CONFIG_DISCOVERY_CLIENT = "zero-config-discovery-client"; @Bean public Store zeroConfigStore() { return new Store(); } @Bean public MessageExecutor zeroConfigMessageExecutor(Self self, StoreService storeService) { return new MessageExecutor(self, storeService); } @Bean public Self zeroConfigSelf() { return new Self(); } @Bean public StoreService zeroConfigStoreService(EventBus eventBus, Store store, DiscoveryClient discoveryClient) { return new StoreService(eventBus, store, discoveryClient); } @Bean public DiscoveryEndpoint zeroConfigDiscoveryEndpoint(Self self) { return new DiscoveryEndpoint(self); } @Bean public RegistryBean zeroConfigDiscoveryServer(Environment environment) { return new RegistryBean().setAppId(BootStrapProperties.readApplication(environment)) .setServiceName(ZERO_CONFIG_DISCOVERY_CLIENT) .addSchemaInterface(ZERO_CONFIG_DISCOVERY_CLIENT, DiscoveryClient.class) // add an empty instance endpoint so that can invoke by endpoint .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(Collections.emptyList())))); } @Bean public DiscoveryClient zeroConfigDiscoveryClient() { return Invoker.createProxy(ZERO_CONFIG_DISCOVERY_CLIENT, ZERO_CONFIG_DISCOVERY_CLIENT, DiscoveryClient.class); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Message.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.io.IOException; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import io.vertx.core.json.jackson.DatabindCodec; public class Message { public static Message of(MessageType type, T body) { return new Message() .setType(type) .setBody(body); } private MessageType type; @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY) @JsonSubTypes( { @Type(name = "REGISTER", value = RegisterRequest.class), @Type(name = "UNREGISTER", value = UnregisterRequest.class) } ) private T body; public MessageType getType() { return type; } public Message setType(MessageType type) { this.type = type; return this; } public T getBody() { return body; } public Message setBody(T body) { this.body = body; return this; } public byte[] encode() throws IOException { return DatabindCodec.mapper().writeValueAsBytes(this); } public static Message decode(byte[] bytes, int length) throws IOException { return DatabindCodec.mapper() .readValue(bytes, 0, length, Message.class); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageExecutor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MessageExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(MessageExecutor.class); private final Self self; private final StoreService storeService; private final Map> messageProcessors = new HashMap<>(); private final ScheduledExecutorService taskExecutor = Executors .newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "lightweight-message-executor")); public MessageExecutor(Self self, StoreService storeService) { this.self = self; this.storeService = storeService; addMessageProcessor(MessageType.REGISTER, this::register); addMessageProcessor(MessageType.UNREGISTER, storeService::unregister); } public void startCheckDeadInstances(Duration interval) { taskExecutor.scheduleAtFixedRate( () -> storeService.deleteDeadInstances(interval), 0, interval.getSeconds(), TimeUnit.SECONDS); } private void register(RegisterRequest request) { if (request.isCrossApp() || Objects.equals(request.getAppId(), self.getAppId())) { storeService.register(request); } } private void addMessageProcessor(MessageType messageType, Consumer messageProcessor) { messageProcessors.put(messageType, messageProcessor); } @SuppressWarnings("unchecked") public void processMessage(Message message) { Consumer consumer = (Consumer) messageProcessors.get(message.getType()); taskExecutor.execute(() -> { try { consumer.accept(message.getBody()); } catch (Throwable e) { LOGGER.error("process message error. ", e); } }); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; public enum MessageType { REGISTER, UNREGISTER } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MicroserviceInfo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.util.Map; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; public class MicroserviceInfo { private Microservice microservice; private MicroserviceInstance instance; private Map schemasById; public Microservice getMicroservice() { return microservice; } public MicroserviceInfo setMicroservice(Microservice microservice) { this.microservice = microservice; return this; } public Map getSchemasById() { return schemasById; } public MicroserviceInfo setSchemasById(Map schemasById) { this.schemasById = schemasById; return this; } public MicroserviceInstance getInstance() { return instance; } public MicroserviceInfo setInstance(MicroserviceInstance instance) { this.instance = instance; return this; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; public class RegisterException extends RuntimeException { private static final long serialVersionUID = 4130899909889771251L; public RegisterException(String message) { super(message); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterInstanceEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; /** * post when a new instance registered
* to notify registration send a new register message
* currently, only "zero config" need this */ public class RegisterInstanceEvent { private final Microservice microservice; private final MicroserviceInstance instance; public RegisterInstanceEvent(Microservice microservice, MicroserviceInstance instance) { this.microservice = microservice; this.instance = instance; } public Microservice getMicroservice() { return microservice; } public MicroserviceInstance getInstance() { return instance; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.util.List; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.invocation.endpoint.EndpointCacheUtils; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; public class RegisterRequest { private String appId; private String serviceId; private boolean crossApp; private String schemasSummary; private String instanceId; private MicroserviceInstanceStatus status; private List endpoints; public String getAppId() { return appId; } public RegisterRequest setAppId(String appId) { this.appId = appId; return this; } public String getServiceId() { return serviceId; } public RegisterRequest setServiceId(String serviceId) { this.serviceId = serviceId; return this; } public boolean isCrossApp() { return crossApp; } public RegisterRequest setCrossApp(boolean crossApp) { this.crossApp = crossApp; return this; } public String getSchemasSummary() { return schemasSummary; } public RegisterRequest setSchemasSummary(String schemasSummary) { this.schemasSummary = schemasSummary; return this; } public String getInstanceId() { return instanceId; } public RegisterRequest setInstanceId(String instanceId) { this.instanceId = instanceId; return this; } public MicroserviceInstanceStatus getStatus() { return status; } public RegisterRequest setStatus(MicroserviceInstanceStatus status) { this.status = status; return this; } public List getEndpoints() { return endpoints; } public RegisterRequest setEndpoints(List endpoints) { this.endpoints = endpoints; return this; } public Endpoint selectFirstEndpoint() { return endpoints.stream() .findFirst() .map(EndpointCacheUtils::getOrCreate) .orElse(null); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/SchemaChangedEvent.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import org.apache.servicecomb.registry.lightweight.model.Microservice; public class SchemaChangedEvent { private final Microservice microservice; public SchemaChangedEvent(Microservice microservice) { this.microservice = microservice; } public Microservice getMicroservice() { return microservice; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceFactory; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.annotations.VisibleForTesting; public class Self implements InitializingBean, BootListener { private Microservice microservice; // Whether to allow cross-app calls to me private boolean crossApp; private MicroserviceInstance instance; private final MicroserviceInfo microserviceInfo = new MicroserviceInfo(); private Environment environment; private RegistrationId registrationId; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setRegistrationId(RegistrationId registrationId) { this.registrationId = registrationId; } @Override public void afterPropertiesSet() { init(environment); } @VisibleForTesting public Self init(Environment environment) { microservice = new MicroserviceFactory().create(environment); microservice.serviceId(String.format("%s/%s/%s/%s", microservice.getEnvironment(), microservice.getAppId(), microservice.getServiceName(), microservice.getVersion())); instance = microservice.getInstance() .instanceId(registrationId.getInstanceId()) .serviceId(microservice.getServiceId()); microserviceInfo .setMicroservice(microservice) .setSchemasById(microservice.getSchemaMap()) .setInstance(instance); return this; } @Override public int getOrder() { return Integer.MAX_VALUE; } @Override public void onBeforeRegistry(BootEvent event) { crossApp = microservice.allowCrossApp(); } public Microservice getMicroservice() { return microservice; } public String getAppId() { return microservice.getAppId(); } public Microservice setServiceName(String serviceName) { return microservice.serviceName(serviceName); } public String getVersion() { return microservice.getVersion(); } public String getInstanceId() { return instance.getInstanceId(); } public String getServiceId() { return instance.getServiceId(); } public MicroserviceInstance getInstance() { return instance; } public MicroserviceInfo getMicroserviceInfo() { return microserviceInfo; } public Self addSchema(String schemaId, String content) { microservice.addSchema(schemaId, content); return this; } public Self addEndpoint(String endpoint) { instance.getEndpoints().add(endpoint); return this; } public RegisterRequest buildRegisterRequest() { return createRegisterRequest() .setAppId(microservice.getAppId()) .setServiceId(microservice.getServiceId()) .setCrossApp(crossApp) .setInstanceId(instance.getInstanceId()) .setStatus(instance.getStatus()) .setEndpoints(instance.getEndpoints()); } protected RegisterRequest createRegisterRequest() { return new RegisterRequest(); } public UnregisterRequest buildUnregisterRequest() { return createUnregisterRequest() .setServiceId(microservice.getServiceId()) .setInstanceId(instance.getInstanceId()); } protected UnregisterRequest createUnregisterRequest() { return new UnregisterRequest(); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/StoreService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import java.time.Duration; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import org.apache.servicecomb.registry.lightweight.store.InstanceStore; import org.apache.servicecomb.registry.lightweight.store.MicroserviceStore; import org.apache.servicecomb.registry.lightweight.store.Store; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; public class StoreService { private static final Logger LOGGER = LoggerFactory.getLogger(StoreService.class); private final EventBus eventBus; private final Store store; private final DiscoveryClient discoveryClient; public StoreService(EventBus eventBus, Store store, DiscoveryClient discoveryClient) { this.store = store; this.discoveryClient = discoveryClient; this.eventBus = eventBus; } public void registerSelf(Self self) { MicroserviceStore microserviceStore = store.addMicroservice(self.getMicroservice()); addInstance(microserviceStore, self.getInstance()); } public InstanceStore register(RegisterRequest request) { return AsyncUtils.toSync(registerAsync(request)); } public CompletableFuture registerAsync(RegisterRequest request) { return doRegisterAsync(request) .whenComplete((r, e) -> logFailedRegister(request, e)); } private void logFailedRegister(RegisterRequest request, Throwable throwable) { if (throwable == null) { return; } LOGGER.error("register instance failed, serviceId={}, instanceId={}, endpoints={}.", request.getServiceId(), request.getInstanceId(), request.getEndpoints(), throwable); } private CompletableFuture doRegisterAsync(RegisterRequest request) { InstanceStore instanceStore = store.findInstanceStore(request.getInstanceId()); if (instanceStore == null) { return addInstance(request); } if (instanceStore.isStatusChanged(request.getStatus())) { updateInstanceStatus(request, instanceStore); } return heartbeat(instanceStore); } private CompletableFuture addInstance(RegisterRequest request) { Endpoint endpoint = request.selectFirstEndpoint(); if (endpoint == null) { return AsyncUtils.completeExceptionally(new RegisterException("can not select endpoint")); } MicroserviceStore microserviceStore = this.store.findMicroserviceStore(request.getServiceId()); if (microserviceStore == null) { return addMicroserviceAndInstance(endpoint, request); } return CompletableFuture.completedFuture(null).thenCompose(v -> discoveryClient.getInstanceAsync(endpoint, request.getServiceId())) .thenApply(instance -> addInstance(microserviceStore, instance)); } private CompletableFuture addMicroserviceAndInstance(Endpoint endpoint, RegisterRequest request) { return discoveryClient.getInfoAsync(endpoint, request.getServiceId()) .thenApply(info -> { info.getMicroservice().getSchemaMap().putAll(info.getSchemasById()); MicroserviceStore microserviceStore = store .addMicroservice(info.getMicroservice()); LOGGER.info("add microservice and instance, serviceId={}, instanceId={}, endpoints={}", request.getServiceId(), request.getInstanceId(), request.getEndpoints()); return doAddInstance(microserviceStore, info.getInstance()); }); } private InstanceStore addInstance(MicroserviceStore microserviceStore, MicroserviceInstance instance) { LOGGER.info("add instance, serviceId={}, instanceId={}, endpoints={}", instance.getServiceId(), instance.getInstanceId(), instance.getEndpoints()); return doAddInstance(microserviceStore, instance); } private InstanceStore doAddInstance(MicroserviceStore microserviceStore, MicroserviceInstance instance) { InstanceStore instanceStore = store.addInstance(microserviceStore, instance); eventBus.post(new RegisterInstanceEvent(microserviceStore.getMicroservice(), instance)); return instanceStore; } private void updateInstanceStatus(RegisterRequest request, InstanceStore instanceStore) { LOGGER.info("update instance status, old status={}, new status={}, serviceId={}, instanceId={}, endpoints={}", instanceStore.getStatus(), request.getStatus(), instanceStore.getServiceId(), instanceStore.getInstanceId(), instanceStore.getEndpoints()); store.findMicroserviceStore(instanceStore.getServiceId()) .updateInstanceStatus(instanceStore, request.getStatus()); } private CompletableFuture heartbeat(InstanceStore instanceStore) { instanceStore.updateLastHeartBeat(); LOGGER.debug("instance heartbeat, serviceId={}, instanceId={}, endpoints={}", instanceStore.getServiceId(), instanceStore.getInstanceId(), instanceStore.getEndpoints()); return CompletableFuture.completedFuture(instanceStore); } public Void unregister(UnregisterRequest request) { return AsyncUtils.toSync(unregisterAsync(request)); } public CompletableFuture unregisterAsync(UnregisterRequest request) { deleteInstance("unregister", request.getServiceId(), request.getInstanceId()); return CompletableFuture.completedFuture(null); } public void deleteInstance(String action, String serviceId, String instanceId) { InstanceStore instanceStore = store.deleteInstance(serviceId, instanceId); if (instanceStore == null) { return; } LOGGER.info("{} instance, serviceId={}, instanceId={}, endpoints={}", action, instanceStore.getServiceId(), instanceStore.getInstanceId(), instanceStore.getEndpoints()); } public void deleteDeadInstances(Duration timeout) { store.findDeadInstances(timeout) .forEach(instance -> deleteInstance("delete dead", instance.getServiceId(), instance.getInstanceId())); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/UnregisterRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; public class UnregisterRequest { private String serviceId; private String instanceId; public String getServiceId() { return serviceId; } public UnregisterRequest setServiceId(String serviceId) { this.serviceId = serviceId; return this; } public String getInstanceId() { return instanceId; } public UnregisterRequest setInstanceId(String instanceId) { this.instanceId = instanceId; return this; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/AbstractPropertiesLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.registry.api.PropertyExtended; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; /** * Loading microservice properties */ public abstract class AbstractPropertiesLoader { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPropertiesLoader.class); public Map loadProperties(Environment environment) { Map propertiesMap = new HashMap<>(); loadPropertiesFromConfigMap(environment, propertiesMap); loadPropertiesFromExtendedClass(environment, propertiesMap); return propertiesMap; } protected abstract Map readProperties(Environment environment); protected abstract String readPropertiesExtendedClass(Environment environment); private void loadPropertiesFromConfigMap(Environment environment, Map propertiesMap) { propertiesMap.putAll(readProperties(environment)); } private void loadPropertiesFromExtendedClass(Environment environment, Map propertiesMap) { String extendedPropertyClass = readPropertiesExtendedClass(environment); if (StringUtils.isEmpty(extendedPropertyClass)) { return; } try { Class classExternalProperty = Class.forName(extendedPropertyClass); if (!PropertyExtended.class.isAssignableFrom(classExternalProperty)) { String errMsg = String.format( "Define propertyExtendedClass %s in yaml, but not implement the interface PropertyExtended.", extendedPropertyClass); LOGGER.error(errMsg); throw new Error(errMsg); } PropertyExtended instance = (PropertyExtended) classExternalProperty.getDeclaredConstructor().newInstance(); Map extendedPropertiesMap = instance.getExtendedProperties(); if (extendedPropertiesMap != null && !extendedPropertiesMap.isEmpty()) { propertiesMap.putAll(extendedPropertiesMap); } } catch (ReflectiveOperationException e) { String errMsg = "Fail to create instance of class: " + extendedPropertyClass; LOGGER.error(errMsg); throw new Error(errMsg, e); } } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/FindInstancesResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.util.List; public class FindInstancesResponse { private List instances; public List getInstances() { return instances; } public FindInstancesResponse setInstances(List instances) { this.instances = instances; return this; } public void mergeInstances(List instances) { if (this.instances == null) { this.instances = instances; } else { this.instances.addAll(instances); } } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheck.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Created by on 2017/1/13. */ @JsonIgnoreProperties(ignoreUnknown = true) public class HealthCheck { private HealthCheckMode mode; private int port; private int interval; private int times; public int getInterval() { return interval; } public void setInterval(int interval) { this.interval = interval; } public int getTimes() { return times; } public void setTimes(int times) { this.times = times; } public HealthCheckMode getMode() { return mode; } public void setMode(HealthCheckMode mode) { this.mode = mode; } public int getTTL() { if (this.mode != HealthCheckMode.HEARTBEAT) { return 0; } return getInterval() * (getTimes() + 1); } public int getPort() { return port; } public void setPort(int port) { this.port = port; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheckMode.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import com.fasterxml.jackson.annotation.JsonValue; /** * Created by on 2017/1/25. */ public enum HealthCheckMode { UNKNOWN("unknown"), HEARTBEAT("push"), PLATFORM("pull"); private final String name; HealthCheckMode(String name) { this.name = name; } @JsonValue public String getName() { return name; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/InstancePropertiesLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.springframework.core.env.Environment; public final class InstancePropertiesLoader extends AbstractPropertiesLoader { public static final InstancePropertiesLoader INSTANCE = new InstancePropertiesLoader(); private InstancePropertiesLoader() { } @Override protected Map readProperties(Environment environment) { return BootStrapProperties.readServiceInstanceProperties(environment); } @Override protected String readPropertiesExtendedClass(Environment environment) { return BootStrapProperties.readServiceInstanceExtendedClass(environment); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/Microservice.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.definition.DefinitionConst; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Created by on 2016/12/5. */ @JsonIgnoreProperties(ignoreUnknown = true) public class Microservice { // service center rule: max length: 64 // two way to generate service id // 1. microservice instance generate by some rule, ex: env/app/name/version // and then register to service center with the id // 2. register to service center with the id to be null, and then service center generate by UUID private String serviceId; private String registerBy; private String environment; // service center rule: max length: 160 private String appId; // service center rule: max length: 128 private String serviceName; /** * for governance * when invoke cross app, if not use alias name, then {microservice}.{schema}.{operation} will conflict */ private String alias; private String version; private String description; private String level; private List schemas = new ArrayList<>(); @JsonIgnore private final Map schemaMap = new HashMap<>(); private Map properties = new HashMap<>(); @JsonIgnore private MicroserviceInstance instance; /** * Currently this field only exists in ServiceComb-Java-Chassis, * and ServiceComb-Service-Center does not hold this field. * Once the 3rd party services are supported to be registered into ServiceComb-Service-Center, * the corresponding field should be added into Service-Center. */ private boolean thirdPartyService; public Microservice() { } public MicroserviceInstance getInstance() { return instance; } public void setInstance(MicroserviceInstance instance) { this.instance = instance; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public Microservice serviceId(String serviceId) { this.serviceId = serviceId; return this; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public Microservice appId(String appId) { this.appId = appId; return this; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public Microservice serviceName(String serviceName) { this.serviceName = serviceName; return this; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Microservice version(String version) { this.version = version; return this; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLevel() { return level; } public void setLevel(String level) { this.level = level; } public List getSchemas() { return schemas; } public void setSchemas(List schemas) { this.schemas = schemas; } public void addSchema(String schemaId, String content) { this.schemaMap.put(schemaId, content); schemas.add(schemaId); } public Map getSchemaMap() { return schemaMap; } public Map getProperties() { return properties; } public void setProperties(Map properties) { this.properties = properties; } public static String generateAbsoluteMicroserviceName(String appId, String microserviceName) { StringBuilder sb = new StringBuilder(appId.length() + microserviceName.length() + 1); sb.append(appId).append(DefinitionConst.APP_SERVICE_SEPARATOR).append(microserviceName); return sb.toString(); } public String getRegisterBy() { return registerBy; } public void setRegisterBy(String registerBy) { this.registerBy = registerBy; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } public boolean isThirdPartyService() { return thirdPartyService; } public void setThirdPartyService(boolean thirdPartyService) { this.thirdPartyService = thirdPartyService; } // Whether to allow cross-app calls to me public boolean allowCrossApp() { return Boolean.parseBoolean(properties.get(DefinitionConst.CONFIG_ALLOW_CROSS_APP_KEY)); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.APP_MAPPING; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_DEFAULT_REGISTER_BY; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.SERVICE_MAPPING; import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.VERSION_MAPPING; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.Version; import org.springframework.core.env.Environment; public class MicroserviceFactory { public Microservice create(Environment environment) { Microservice microservice = createMicroserviceFromConfiguration(environment); microservice.setInstance(MicroserviceInstance.createFromDefinition(environment)); return microservice; } private Microservice createMicroserviceFromConfiguration(Environment environment) { Microservice microservice = new Microservice(); if (!StringUtils.isEmpty(environment.getProperty(APP_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(APP_MAPPING)))) { microservice.setAppId(environment.getProperty(environment.getProperty(APP_MAPPING))); } else { microservice.setAppId(BootStrapProperties.readApplication(environment)); } if (!StringUtils.isEmpty(environment.getProperty(SERVICE_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(SERVICE_MAPPING)))) { microservice.setServiceName(environment.getProperty(environment.getProperty(SERVICE_MAPPING))); } else { microservice.setServiceName(BootStrapProperties.readServiceName(environment)); } String version; if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { version = environment.getProperty(environment.getProperty(VERSION_MAPPING)); } else { version = BootStrapProperties.readServiceVersion(environment); } // just check version format new Version(version); microservice.setVersion(version); microservice.setDescription(BootStrapProperties.readServiceDescription(environment)); microservice.setLevel(BootStrapProperties.readServiceRole(environment)); Map propertiesMap = MicroservicePropertiesLoader.INSTANCE.loadProperties(environment); microservice.setProperties(propertiesMap); microservice.setEnvironment(BootStrapProperties.readServiceEnvironment(environment)); // set alias name when allow cross app if (microservice.allowCrossApp()) { microservice.setAlias(Microservice.generateAbsoluteMicroserviceName(microservice.getAppId(), microservice.getServiceName())); } microservice.setRegisterBy(CONFIG_DEFAULT_REGISTER_BY); return microservice; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.springframework.core.env.Environment; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Created by on 2016/12/5. */ @JsonIgnoreProperties(ignoreUnknown = true) public class MicroserviceInstance { // even disconnected from service center // instanceId will not be changed // when register to service center again, use the old instanceId. private String instanceId; // service center rule: max length: 64 private String serviceId; private List endpoints = new ArrayList<>(); private String hostName; private MicroserviceInstanceStatus status = MicroserviceInstanceStatus.UP; private Map properties = new HashMap<>(); // reserved key list: region|az|stage|group private HealthCheck healthCheck; private String stage; private DataCenterInfo dataCenterInfo; private String timestamp; @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("instanceId=" + instanceId + ";"); sb.append("serviceId=" + serviceId + ";"); sb.append("status=" + status + ";"); sb.append("endpoints=" + endpoints.toString()); return sb.toString(); } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public MicroserviceInstance instanceId(String instanceId) { this.instanceId = instanceId; return this; } public String getServiceId() { return serviceId; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public MicroserviceInstance serviceId(String serviceId) { this.serviceId = serviceId; return this; } public String getHostName() { return hostName; } public void setHostName(String hostName) { this.hostName = hostName; } public List getEndpoints() { return endpoints; } public void setEndpoints(List endpoints) { this.endpoints = endpoints; } public MicroserviceInstanceStatus getStatus() { return status; } public void setStatus(MicroserviceInstanceStatus status) { this.status = status; } public Map getProperties() { return properties; } public void setProperties(Map properties) { this.properties = properties; } public HealthCheck getHealthCheck() { return healthCheck; } public void setHealthCheck(HealthCheck healthCheck) { this.healthCheck = healthCheck; } @Deprecated public String getStage() { return stage; } @Deprecated public void setStage(String stage) { this.stage = stage; } public DataCenterInfo getDataCenterInfo() { return dataCenterInfo; } @Override public int hashCode() { return this.instanceId.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof MicroserviceInstance) { return this.instanceId.equals(((MicroserviceInstance) obj).instanceId); } return false; } public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { this.dataCenterInfo = dataCenterInfo; } // Some properties of microservice instance are dynamic changed, not cover them all now. public static MicroserviceInstance createFromDefinition(Environment environment) { MicroserviceInstance microserviceInstance = new MicroserviceInstance(); // default hard coded values microserviceInstance.setStage(DefinitionConst.DEFAULT_STAGE); microserviceInstance.setStatus(MicroserviceInstanceStatus .valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); HealthCheck healthCheck = new HealthCheck(); healthCheck.setMode(HealthCheckMode.HEARTBEAT); microserviceInstance.setHealthCheck(healthCheck); // load properties Map propertiesMap = InstancePropertiesLoader.INSTANCE.loadProperties(environment); microserviceInstance.setProperties(propertiesMap); // load data center information loadDataCenterInfo(environment, microserviceInstance); return microserviceInstance; } private static void loadDataCenterInfo(Environment environment, MicroserviceInstance microserviceInstance) { String dataCenterName = environment.getProperty("servicecomb.datacenter.name"); if (StringUtils.isEmpty(dataCenterName)) { return; } String region = environment.getProperty("servicecomb.datacenter.region"); String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setName(dataCenterName); dataCenterInfo.setRegion(region); dataCenterInfo.setAvailableZone(availableZone); microserviceInstance.setDataCenterInfo(dataCenterInfo); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstances.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.nio.charset.StandardCharsets; import javax.crypto.Mac; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.HmacAlgorithms; import org.apache.commons.codec.digest.HmacUtils; import org.apache.commons.lang3.StringUtils; public class MicroserviceInstances { private boolean microserviceNotExist; private boolean needRefresh = true; private String revision; private FindInstancesResponse instancesResponse; public boolean isMicroserviceNotExist() { return microserviceNotExist; } public MicroserviceInstances setMicroserviceNotExist(boolean microserviceNotExist) { this.microserviceNotExist = microserviceNotExist; return this; } public boolean isNeedRefresh() { return needRefresh; } public MicroserviceInstances setNeedRefresh(boolean needRefresh) { this.needRefresh = needRefresh; return this; } public String getRevision() { return revision; } public MicroserviceInstances setRevision(String revision) { this.revision = revision; return this; } public FindInstancesResponse getInstancesResponse() { return instancesResponse; } public MicroserviceInstances setInstancesResponse(FindInstancesResponse instancesResponse) { this.instancesResponse = instancesResponse; return this; } public void mergeMicroserviceInstances(MicroserviceInstances other) { mergeNeedRefresh(other.needRefresh); mergeMicroserviceNotExist(other.microserviceNotExist); mergeRevision(other); mergeInstanceResponse(other.getInstancesResponse()); } private void mergeRevision(MicroserviceInstances other) { if (!other.isMicroserviceNotExist() && other.needRefresh) { Mac mac = HmacUtils.getInitializedMac(HmacAlgorithms.HMAC_SHA_1, stringToBytes(this.revision)); this.revision = Base64.encodeBase64String(mac.doFinal(stringToBytes(other.revision))); } } private byte[] stringToBytes(String input) { if (StringUtils.isEmpty(input)) { input = "@"; } return input.getBytes(StandardCharsets.UTF_8); } private void mergeMicroserviceNotExist(boolean microserviceNotExist) { // only is all not exists, set to not exits. if (this.microserviceNotExist) { this.microserviceNotExist = microserviceNotExist; } } private void mergeNeedRefresh(boolean needRefresh) { // if one of discovery need refresh, all need refresh if (!this.needRefresh) { this.needRefresh = needRefresh; } } private void mergeInstanceResponse(FindInstancesResponse instancesResponse) { if (instancesResponse == null) { return; } if (this.instancesResponse == null) { this.instancesResponse = instancesResponse; return; } this.instancesResponse.mergeInstances(instancesResponse.getInstances()); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroservicePropertiesLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.model; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.springframework.core.env.Environment; public final class MicroservicePropertiesLoader extends AbstractPropertiesLoader { public static final MicroservicePropertiesLoader INSTANCE = new MicroservicePropertiesLoader(); private MicroservicePropertiesLoader() { } @Override protected Map readProperties(Environment environment) { return BootStrapProperties.readServiceProperties(environment); } @Override protected String readPropertiesExtendedClass(Environment environment) { return BootStrapProperties.readServiceExtendedClass(environment); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/AppStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.store; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; public class AppStore { private final Map microserviceByName = new ConcurrentHashMapEx<>(); public void addMicroservice(MicroserviceStore microserviceStore) { microserviceByName.put(microserviceStore.getServiceName(), microserviceStore); } public MicroserviceInstances findServiceInstances(String serviceName, String revision) { MicroserviceStore microserviceStore = microserviceByName.get(serviceName); if (microserviceStore == null) { return new MicroserviceInstances() .setMicroserviceNotExist(true); } return microserviceStore.findServiceInstances(revision); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/InstanceStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.store; import java.util.List; import java.util.Objects; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import com.google.common.base.Ticker; public class InstanceStore { private final Ticker ticker; private MicroserviceInstance instance; private long lastHeartBeat; public InstanceStore(Ticker ticker, MicroserviceInstance instance) { this.ticker = ticker; this.instance = instance; this.updateLastHeartBeat(); } public boolean isStatusChanged(MicroserviceInstanceStatus status) { return !Objects.equals(instance.getStatus(), status); } public MicroserviceInstance getInstance() { return instance; } public InstanceStore setInstance(MicroserviceInstance instance) { this.instance = instance; return this; } public String getInstanceId() { return getInstance().getInstanceId(); } public String getServiceId() { return getInstance().getServiceId(); } public List getEndpoints() { return getInstance().getEndpoints(); } public MicroserviceInstanceStatus getStatus() { return instance.getStatus(); } public void setStatus(MicroserviceInstanceStatus status) { instance.setStatus(status); } public void updateLastHeartBeat() { this.lastHeartBeat = ticker.read(); } public long getLastHeartBeat() { return lastHeartBeat; } public boolean isHeartBeatTimeout(long nanoNow, long nanoTimeout) { return (nanoNow - lastHeartBeat) > nanoTimeout; } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/MicroserviceStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.store; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.lightweight.model.FindInstancesResponse; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; import com.google.common.base.Ticker; public class MicroserviceStore { private final Ticker ticker; private final Microservice microservice; private final Map instancesById = new ConcurrentHashMapEx<>(); private String instancesRevision; public MicroserviceStore(Ticker ticker, Microservice microservice) { this.ticker = ticker; this.microservice = microservice; updateInstancesRevision(); } public void updateInstancesRevision() { this.instancesRevision = String.valueOf(ticker.read()); } public String getServiceName() { return microservice.getServiceName(); } public String getServiceId() { return microservice.getServiceId(); } public Microservice getMicroservice() { return microservice; } public InstanceStore addInstance(MicroserviceInstance instance) { InstanceStore instanceStore = new InstanceStore(ticker, instance); instancesById.put(instance.getInstanceId(), instanceStore); updateInstancesRevision(); return instanceStore; } public InstanceStore deleteInstance(String instanceId) { InstanceStore instanceStore = instancesById.remove(instanceId); if (instanceStore != null) { updateInstancesRevision(); } return instanceStore; } public void updateInstanceStatus(InstanceStore instanceStore, MicroserviceInstanceStatus status) { instanceStore.setStatus(status); updateInstancesRevision(); } public MicroserviceInstances findServiceInstances(String revision) { if (instancesRevision.equals(revision)) { return new MicroserviceInstances() .setRevision(instancesRevision) .setNeedRefresh(false); } List instances = instancesById.values().stream() .map(InstanceStore::getInstance) .collect(Collectors.toList()); FindInstancesResponse response = new FindInstancesResponse() .setInstances(instances); return new MicroserviceInstances() .setRevision(instancesRevision) .setInstancesResponse(response); } public boolean hasInstance() { return !instancesById.isEmpty(); } public int getInstanceCount() { return instancesById.size(); } } ================================================ FILE: service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/Store.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight.store; import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; import com.google.common.base.Ticker; public class Store { private Ticker ticker = Ticker.systemTicker(); private final Map appsById = new ConcurrentHashMapEx<>(); private final Map microservicesByName = new ConcurrentHashMapEx<>(); private final Map microservicesById = new ConcurrentHashMapEx<>(); private final Map instancesById = new ConcurrentHashMapEx<>(); public Store setTicker(Ticker ticker) { this.ticker = ticker; return this; } public AppStore getOrCreateAppStore(String appId) { return appsById.computeIfAbsent(appId, key -> new AppStore()); } public MicroserviceStore findMicroserviceStore(String serviceId) { return microservicesById.get(serviceId); } public InstanceStore findInstanceStore(String instanceId) { return instancesById.get(instanceId); } public MicroserviceStore addMicroservice(Microservice microservice) { MicroserviceStore microserviceStore = new MicroserviceStore(ticker, microservice); getOrCreateAppStore(microservice.getAppId()) .addMicroservice(microserviceStore); microservicesById.put(microservice.getServiceId(), microserviceStore); microservicesByName.put(microservice.getServiceName(), microserviceStore); return microserviceStore; } public InstanceStore addInstance(MicroserviceStore microserviceStore, MicroserviceInstance instance) { InstanceStore instanceStore = microserviceStore.addInstance(instance); instancesById.put(instance.getInstanceId(), instanceStore); return instanceStore; } public InstanceStore deleteInstance(String serviceId, String instanceId) { MicroserviceStore microserviceStore = findMicroserviceStore(serviceId); if (microserviceStore == null) { return null; } InstanceStore instanceStore = microserviceStore.deleteInstance(instanceId); if (instanceStore != null) { instancesById.remove(instanceId); } return instanceStore; } public Optional getMicroservice(String microserviceId) { return Optional.ofNullable(microservicesById.get(microserviceId)) .map(MicroserviceStore::getMicroservice); } public Optional getMicroserviceInstance(String instanceId) { return Optional.ofNullable(instancesById.get(instanceId)) .map(InstanceStore::getInstance); } public MicroserviceInstances findServiceInstances(String appId, String serviceName, String revision) { return Optional.ofNullable(appsById.get(appId)) .map(appStore -> appStore.findServiceInstances(serviceName, revision)) .orElse(new MicroserviceInstances() .setMicroserviceNotExist(true)); } public List getAllMicroservices() { return microservicesByName.values().stream() .map(MicroserviceStore::getMicroservice) .collect(Collectors.toList()); } public int getMicroserviceCount() { return microservicesByName.size(); } public int getInstanceCount() { return instancesById.size(); } public Stream findDeadInstances(Duration timeout) { long nanoNow = ticker.read(); long nanoTimeout = timeout.toNanos(); return instancesById.values().stream() .filter(instanceStore -> instanceStore.isHeartBeatTimeout(nanoNow, nanoTimeout)) .map(InstanceStore::getInstance); } } ================================================ FILE: service-registry/registry-lightweight/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.lightweight.LightWeightRegistryConfiguration ================================================ FILE: service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.config; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.registry.lightweight.model.AbstractPropertiesLoader; import org.apache.servicecomb.registry.lightweight.model.MicroservicePropertiesLoader; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; public class TestAbstractPropertiesLoader { @Test public void testExtendedClassCompatible() { ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); EnumerablePropertySource propertySource = Mockito.mock(EnumerablePropertySource.class); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] {}); Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_EXTENDED_CLASS)) .thenReturn("invalidClass"); AbstractPropertiesLoader loader = MicroservicePropertiesLoader.INSTANCE; try { loader.loadProperties(environment); Assertions.fail("Must throw exception"); } catch (Error e) { Assertions.assertEquals(ClassNotFoundException.class, e.getCause().getClass()); Assertions.assertEquals("invalidClass", e.getCause().getMessage()); } } } ================================================ FILE: service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; import org.skyscreamer.jsonassert.JSONAssert; class MessageTest { private String toLinuxPrettyJson(Object value) { return Json.encodePrettily(value) .replaceAll("\r\n", "\n"); } @Test void should_encode_register_type() { Message msg = Message.of(MessageType.REGISTER, new RegisterRequest()); try { JSONAssert.assertEquals(toLinuxPrettyJson(msg), "" + "{\n" + " \"type\" : \"REGISTER\",\n" + " \"body\" : {\n" + " \"appId\" : null,\n" + " \"serviceId\" : null,\n" + " \"crossApp\" : false,\n" + " \"schemasSummary\" : null,\n" + " \"instanceId\" : null,\n" + " \"status\" : null,\n" + " \"endpoints\" : null\n" + " }\n" + "}", false); } catch (Exception e) { fail("Failed to compare JSONs: " + e.getMessage(), e); } } @Test void should_decode_register_type() { String json = Json.encode(Message.of(MessageType.REGISTER, new RegisterRequest())); Message message = Json.decodeValue(json, Message.class); assertThat(message.getBody()).isInstanceOf(RegisterRequest.class); } @Test void should_encode_unregister_type() { Message msg = Message.of(MessageType.UNREGISTER, new UnregisterRequest()); try { JSONAssert.assertEquals(toLinuxPrettyJson(msg), "" + "{\n" + " \"type\" : \"UNREGISTER\",\n" + " \"body\" : {\n" + " \"serviceId\" : null,\n" + " \"instanceId\" : null\n" + " }\n" + "}", false); } catch (Exception e) { fail("Failed to compare JSONs: " + e.getMessage(), e); } } @Test void should_decode_unregister_type() { String json = Json.encode(Message.of(MessageType.UNREGISTER, new UnregisterRequest())); Message message = Json.decodeValue(json, Message.class); assertThat(message.getBody()).isInstanceOf(UnregisterRequest.class); } } ================================================ FILE: service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.lightweight; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.foundation.test.scaffolding.time.MockTicker; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.store.InstanceStore; import org.apache.servicecomb.registry.lightweight.store.Store; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MutablePropertySources; import com.google.common.eventbus.EventBus; import io.vertx.core.json.Json; public class StoreServiceTest { static Endpoint endpoint = Mockito.mock(Endpoint.class); static class MockRegisterRequest extends RegisterRequest { @Override public Endpoint selectFirstEndpoint() { return endpoint; } } static ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); Self self; EventBus eventBus = new EventBus(); MockTicker ticker = new MockTicker(); Store store = new Store().setTicker(ticker); DiscoveryClient discoveryClient = Mockito.mock(DiscoveryClient.class); StoreService service = new StoreService(eventBus, store, discoveryClient); @BeforeEach void setUp() { MutablePropertySources mutablePropertySources = new MutablePropertySources(); EnumerablePropertySource propertySource = Mockito.mock(EnumerablePropertySource.class); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] {}); Mockito.when(environment.getProperty("servicecomb.service.application")).thenReturn("app"); Mockito.when(environment.getProperty("servicecomb.service.name")).thenReturn("svc"); Mockito.when(environment.getProperty("servicecomb.service.version")).thenReturn("1.0.0.0"); Mockito.when(environment.getProperty("servicecomb.instance.initialStatus")).thenReturn("UP"); self = new Self() { @Override protected RegisterRequest createRegisterRequest() { return new MockRegisterRequest(); } }; self.setRegistrationId(new RegistrationId()); self.init(environment) .addSchema("schema-1", "s1") .addEndpoint("rest://1.1.1.1:80"); Mockito.when(discoveryClient.getInfoAsync(ArgumentMatchers.any(), ArgumentMatchers.any())) .thenReturn(CompletableFuture.completedFuture(self.getMicroserviceInfo())); Mockito.when(discoveryClient.getInstanceAsync(ArgumentMatchers.any(), ArgumentMatchers.any())) .thenReturn(CompletableFuture.completedFuture(self.getInstance())); } @Test void should_register_microservice_and_instance_when_both_not_exist() { RegisterRequest request = self.buildRegisterRequest(); InstanceStore instanceStore = service.register(request); assertThat(store.findMicroserviceStore(self.getServiceId()).getMicroservice()) .isSameAs(self.getMicroservice()); assertThat(instanceStore.getInstance()).isSameAs(self.getInstance()); assertThat(self.getInstance().getStatus()).isEqualTo(MicroserviceInstanceStatus.UP); } @Test void should_register_instance_when_microservice_exist() { Microservice microservice = Json.decodeValue(Json.encode(self.getMicroservice()), Microservice.class); store.addMicroservice(microservice); RegisterRequest request = self.buildRegisterRequest(); InstanceStore instanceStore = service.register(request); assertThat(microservice).isNotSameAs(self.getMicroservice()); assertThat(store.findMicroserviceStore(self.getServiceId()).getMicroservice()) .isSameAs(microservice); assertThat(instanceStore.getInstance()).isSameAs(self.getInstance()); } @Test void should_allow_update_instance_status() { should_register_microservice_and_instance_when_both_not_exist(); RegisterRequest request = self.buildRegisterRequest() .setStatus(MicroserviceInstanceStatus.TESTING); ticker.setValues(1L); InstanceStore instanceStore = service.register(request); assertThat(self.getInstance().getStatus()).isEqualTo(MicroserviceInstanceStatus.TESTING); assertThat(instanceStore.getLastHeartBeat()).isEqualTo(1); } @Test void should_process_as_heartbeat_when_nothing_changed() { should_register_microservice_and_instance_when_both_not_exist(); InstanceStore instanceStore = store.findInstanceStore(self.getInstanceId()); assertThat(instanceStore.getLastHeartBeat()).isEqualTo(0); ticker.setValues(1L); should_register_microservice_and_instance_when_both_not_exist(); assertThat(instanceStore.getLastHeartBeat()).isEqualTo(1); } } ================================================ FILE: service-registry/registry-local/pom.xml ================================================ org.apache.servicecomb service-registry-parent 3.4.0-SNAPSHOT 4.0.0 registry-local Java Chassis::Java Chassis::Service Registry::Local org.apache.servicecomb foundation-vertx org.apache.servicecomb foundation-config org.apache.servicecomb foundation-common org.apache.servicecomb swagger-generator-core org.apache.servicecomb java-chassis-core org.apache.servicecomb foundation-registry org.apache.commons commons-lang3 io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import java.util.List; import org.apache.servicecomb.registry.api.Discovery; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class LocalDiscovery implements Discovery { public static final String LOCAL_DISCOVERY_ENABLED = "servicecomb.registry.local.%s.%s.enabled"; private LocalRegistryStore localRegistryStore; private Environment environment; @Autowired public void setLocalRegistryStore(LocalRegistryStore localRegistryStore) { this.localRegistryStore = localRegistryStore; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public int getOrder() { return -10000; } @Override public String name() { return LocalRegistryConst.LOCAL_REGISTRY_NAME; } @Override public boolean enabled(String application, String serviceName) { return environment.getProperty(String.format(LOCAL_DISCOVERY_ENABLED, application, serviceName), boolean.class, true); } @Override public List findServiceInstances(String application, String serviceName) { return this.localRegistryStore.findServiceInstances(application, serviceName); } @Override public List findServices(String application) { return this.localRegistryStore.findServices(application); } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { } @Override public void init() { this.localRegistryStore.init(); } @Override public void run() { this.localRegistryStore.run(); } @Override public void destroy() { } @Override public boolean enabled() { return this.environment.getProperty(LocalRegistryConst.LOCAL_REGISTRY_ENABLED, boolean.class, true); } } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import java.lang.management.ManagementFactory; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.oas.models.OpenAPI; public class LocalDiscoveryInstance extends AbstractDiscoveryInstance { private static final AtomicLong INSTANCE_ID = new AtomicLong(); private final RegistryBean registryBean; private final LocalRegistrationInstance localRegistrationInstance; private final List endpoints; private final String instanceId; public LocalDiscoveryInstance(LocalOpenAPIRegistry localOpenAPIRegistry, RegistryBean registryBean, List endpoints, LocalRegistrationInstance localRegistrationInstance) { this.registryBean = registryBean; this.localRegistrationInstance = localRegistrationInstance; this.endpoints = endpoints; this.instanceId = System.currentTimeMillis() + "-" + ManagementFactory.getRuntimeMXBean().getPid() + "-" + INSTANCE_ID.getAndIncrement(); registryBean.getSchemaInterfaces().forEach((k, v) -> { SwaggerGenerator generator = SwaggerGenerator.create(v); OpenAPI openAPI = generator.generate(); if (openAPI == null) { throw new IllegalStateException(String.format("Generate schema for %s/%s/%s failed.", registryBean.getAppId(), registryBean.getServiceName(), k)); } String schemaContent = SwaggerUtils.swaggerToString(openAPI); if (StringUtils.isEmpty(schemaContent)) { throw new IllegalStateException(String.format("Generate schema for %s/%s/%s failed.", registryBean.getAppId(), registryBean.getServiceName(), k)); } localOpenAPIRegistry.registerOpenAPI(registryBean.getAppId(), registryBean.getServiceName(), k, openAPI); }); } public LocalDiscoveryInstance(LocalRegistrationInstance registrationInstance) { this.registryBean = new RegistryBean(); this.registryBean.setAppId(registrationInstance.getApplication()); this.registryBean.setServiceName(registrationInstance.getServiceName()); this.registryBean.setVersion(registrationInstance.getVersion()); this.localRegistrationInstance = registrationInstance; this.endpoints = registrationInstance.getEndpoints(); this.instanceId = registrationInstance.getInstanceId(); } @Override public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.UP; } @Override public String getRegistryName() { return LocalRegistryConst.LOCAL_REGISTRY_NAME; } @Override public String getEnvironment() { return localRegistrationInstance.getEnvironment(); } @Override public String getApplication() { return registryBean.getAppId(); } @Override public String getServiceName() { return registryBean.getServiceName(); } @Override public String getAlias() { return null; } @Override public String getVersion() { return registryBean.getVersion(); } @Override public DataCenterInfo getDataCenterInfo() { return null; } @Override public String getDescription() { return null; } @Override public Map getProperties() { return null; } @Override public Map getSchemas() { // not implement return Collections.emptyMap(); } @Override public List getEndpoints() { return endpoints; } @Override public String getInstanceId() { return instanceId; } } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class LocalRegistration implements Registration { private final LocalRegistrationInstance localRegistrationInstance; private Environment environment; public LocalRegistration(LocalRegistrationInstance localRegistrationInstance) { this.localRegistrationInstance = localRegistrationInstance; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void init() { } @Override public void run() { } @Override public void destroy() { } @Override public String name() { return LocalRegistryConst.LOCAL_REGISTRY_NAME; } @Override public LocalRegistrationInstance getMicroserviceInstance() { return localRegistrationInstance; } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { return true; } @Override public void addSchema(String schemaId, String content) { // not implement } @Override public void addEndpoint(String endpoint) { localRegistrationInstance.addEndpoint(endpoint); } @Override public void addProperty(String key, String value) { localRegistrationInstance.addProperty(key, value); } @Override public boolean enabled() { return this.environment.getProperty(LocalRegistryConst.LOCAL_REGISTRY_ENABLED, Boolean.class, true); } } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.springframework.core.env.Environment; public class LocalRegistrationInstance implements RegistrationInstance { private final Environment environment; private final DataCenterInfo dataCenterInfo; private final LocalOpenAPIRegistry localOpenAPIRegistry; private final String instanceId; private final Map schemas = new HashMap<>(); private final List endpoints = new ArrayList<>(); private final Map properties = new HashMap<>(); public LocalRegistrationInstance( Environment environment, DataCenterProperties dataCenterProperties, LocalOpenAPIRegistry localOpenAPIRegistry, RegistrationId registrationId) { this.environment = environment; this.localOpenAPIRegistry = localOpenAPIRegistry; this.dataCenterInfo = new DataCenterInfo(); this.dataCenterInfo.setName(dataCenterProperties.getName()); this.dataCenterInfo.setRegion(dataCenterProperties.getRegion()); this.dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); this.properties.putAll(BootStrapProperties.readServiceProperties(environment)); this.instanceId = registrationId.getInstanceId(); } @Override public String getEnvironment() { return BootStrapProperties.readServiceEnvironment(environment); } @Override public String getApplication() { return BootStrapProperties.readApplication(environment); } @Override public String getServiceName() { return BootStrapProperties.readServiceName(environment); } @Override public String getAlias() { return BootStrapProperties.readServiceAlias(environment); } @Override public String getVersion() { return BootStrapProperties.readServiceVersion(environment); } @Override public DataCenterInfo getDataCenterInfo() { return dataCenterInfo; } @Override public String getDescription() { return BootStrapProperties.readServiceDescription(environment); } @Override public Map getProperties() { return this.properties; } @Override public Map getSchemas() { return this.schemas; } @Override public List getEndpoints() { return this.endpoints; } @Override public String getInstanceId() { return this.instanceId; } @Override public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.UP; } public void addEndpoint(String endpoint) { this.endpoints.add(endpoint); } public void addProperty(String key, String value) { this.properties.put(key, value); } } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.registry.RegistrationId; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class LocalRegistryConfiguration { @Bean public LocalRegistrationInstance localRegistrationInstance( Environment environment, DataCenterProperties dataCenterProperties, LocalOpenAPIRegistry localOpenAPIRegistry, RegistrationId registrationId) { return new LocalRegistrationInstance(environment, dataCenterProperties, localOpenAPIRegistry, registrationId); } @Bean public LocalRegistration localRegistration(LocalRegistrationInstance localRegistrationInstance) { return new LocalRegistration(localRegistrationInstance); } @Bean public LocalDiscovery localDiscovery() { return new LocalDiscovery(); } @Bean public LocalRegistryStore localRegistryStore() { return new LocalRegistryStore(); } } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; public class LocalRegistryConst { public static final String LOCAL_REGISTRY_ENABLED = "servicecomb.registry.local.enabled"; public static final String LOCAL_REGISTRY_NAME = "local-registry"; } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.config.YAMLUtil; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.common.utils.JvmUtils; import org.apache.servicecomb.localregistry.RegistryBean.Instance; import org.springframework.beans.factory.annotation.Autowired; public class LocalRegistryStore { private static final String REGISTRY_FILE_NAME = "registry.yaml"; private LocalRegistrationInstance selfMicroserviceInstance; private LocalOpenAPIRegistry localOpenAPIRegistry; // application:serviceName private final Map>> microserviceInstanceMap = new ConcurrentHashMap<>(); public LocalRegistryStore() { } @Autowired public void setLocalRegistrationInstance(LocalRegistrationInstance selfMicroserviceInstance) { this.selfMicroserviceInstance = selfMicroserviceInstance; } @Autowired public void setLocalOpenAPIRegistry(LocalOpenAPIRegistry localOpenAPIRegistry) { this.localOpenAPIRegistry = localOpenAPIRegistry; } public void init() { microserviceInstanceMap.clear(); } public void run() { List beans = loadYamlBeans(); BeanUtils.getBeansOfType(RegistryBean.class).forEach((key, value) -> beans.add(value)); initRegistryFromBeans(beans); addSelf(); } private void addSelf() { microserviceInstanceMap.computeIfAbsent(selfMicroserviceInstance.getApplication(), key -> new ConcurrentHashMapEx<>()).computeIfAbsent(selfMicroserviceInstance.getServiceName(), key -> new ArrayList<>()).add(new LocalDiscoveryInstance(selfMicroserviceInstance)); } private List loadYamlBeans() { List beans = new ArrayList<>(); try { ClassLoader loader = JvmUtils.findClassLoader(); Enumeration urls = loader.getResources(REGISTRY_FILE_NAME); while (urls.hasMoreElements()) { URL url = urls.nextElement(); try (InputStream is = url.openStream()) { if (is != null) { beans.addAll(initFromData(is)); } } } } catch (IOException e) { throw new IllegalStateException(e); } return beans; } private List initFromData(InputStream is) { Map data = YAMLUtil.yaml2Properties(is); return initFromData(data); } @SuppressWarnings("unchecked") private List initFromData(Map data) { List beans = new ArrayList<>(); for (Entry entry : data.entrySet()) { String name = entry.getKey(); List> serviceConfigs = (List>) entry.getValue(); for (Map serviceConfig : serviceConfigs) { beans.add(RegistryBean.buildFromYamlModel(name, serviceConfig)); } } return beans; } private void initRegistryFromBeans(List beans) { beans.forEach((bean -> { List instances = microserviceInstanceMap.computeIfAbsent(bean.getAppId(), key -> new ConcurrentHashMapEx<>()).computeIfAbsent(bean.getServiceName(), key -> new ArrayList<>()); if (bean.getInstances() == null) { return; } for (Instance instance : bean.getInstances().getInstances()) { instances.add(new LocalDiscoveryInstance(localOpenAPIRegistry, bean, instance.getEndpoints(), selfMicroserviceInstance)); } })); } public List findServiceInstances(String application, String serviceName) { Map> app = microserviceInstanceMap.get(application); if (app == null) { return Collections.emptyList(); } List instances = app.get(serviceName); if (instances == null) { return Collections.emptyList(); } return instances; } public List findServices(String application) { if (microserviceInstanceMap.get(application) == null) { return Collections.emptyList(); } return microserviceInstanceMap.get(application).keySet().stream().toList(); } } ================================================ FILE: service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/RegistryBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.localregistry; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; /** * * Configuration bean for local services. Bean configuration is token * same as `registry.yaml` file configuration. * */ public class RegistryBean { public static class Instances { private List instances; public List getInstances() { return instances; } public Instances setInstances(List instances) { this.instances = instances; return this; } } public static class Instance { private List endpoints; public List getEndpoints() { return endpoints; } public Instance setEndpoints(List endpoints) { this.endpoints = endpoints; return this; } } private String id; private String serviceName; private String version; private String appId; /** * Schema ids configured from RegistryBean. Generate schema content from Class. */ private final Map> schemaInterfaces = new HashMap<>(); /** * Schema ids configured in yaml. Will load contents from local file. */ private List schemaIds = new ArrayList<>(); private Instances instances; @SuppressWarnings("unchecked") public static RegistryBean buildFromYamlModel (String serviceName, Map serviceConfig) { return new RegistryBean() .setId(validId((String) serviceConfig.get("id"))) .setServiceName(serviceName) .setVersion((String) serviceConfig.get("version")) .setAppId(validAppId((String) serviceConfig.get("appid"))) .setSchemaIds(validListsValue((List) serviceConfig.get("schemaIds"))) .setInstances( new Instances() .setInstances(validInstances((List>) serviceConfig.get("instances")))); } @SuppressWarnings("unchecked") private static List validInstances(List> instancesConfig) { if (instancesConfig == null) { return Collections.emptyList(); } List instances = new ArrayList<>(); for (Map instanceConfig : instancesConfig) { instances.add(new Instance().setEndpoints( validListsValue((List) instanceConfig.get("endpoints")))); } return instances; } private static List validListsValue(List listsValue) { return listsValue == null ? Collections.emptyList() : listsValue; } private static String validId(String serviceId) { return StringUtils.isEmpty(serviceId) ? UUID.randomUUID().toString() : serviceId; } private static String validAppId(String configAppId) { if (!StringUtils.isEmpty(configAppId)) { return configAppId; } return BootStrapProperties.DEFAULT_APPLICATION; } public String getId() { return id; } public RegistryBean setId(String id) { this.id = id; return this; } public String getServiceName() { return serviceName; } public RegistryBean setServiceName(String serviceName) { this.serviceName = serviceName; return this; } public String getVersion() { return version; } public RegistryBean setVersion(String version) { this.version = version; return this; } public String getAppId() { return appId; } public RegistryBean setAppId(String appId) { this.appId = appId; return this; } public RegistryBean addSchemaInterface(String schemaId, Class schemaInterface) { this.schemaInterfaces.put(schemaId, schemaInterface); return this; } public RegistryBean addSchemaId(String schemaId) { this.schemaIds.add(schemaId); return this; } public RegistryBean setSchemaIds(List schemaIds) { this.schemaIds = schemaIds; return this; } public List getSchemaIds() { return this.schemaIds; } public Map> getSchemaInterfaces() { return this.schemaInterfaces; } public Instances getInstances() { return instances; } public RegistryBean setInstances(Instances instances) { this.instances = instances; return this; } } ================================================ FILE: service-registry/registry-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.localregistry.LocalRegistryConfiguration ================================================ FILE: service-registry/registry-nacos/pom.xml ================================================ org.apache.servicecomb service-registry-parent 3.4.0-SNAPSHOT 4.0.0 registry-nacos Java Chassis::Service Registry::Nacos com.alibaba.nacos nacos-client org.apache.servicecomb foundation-common org.apache.servicecomb foundation-registry org.apache.servicecomb java-chassis-core ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.registry.RegistrationId; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class NacosConfiguration { @Bean @ConfigurationProperties(prefix = NacosConst.NACOS_REGISTRY_PREFIX) public NacosDiscoveryProperties nacosDiscoveryProperties() { return new NacosDiscoveryProperties(); } @Bean public NacosRegistration nacosRegistration( DataCenterProperties dataCenterProperties, @Qualifier("nacosDiscoveryProperties") NacosDiscoveryProperties nacosDiscoveryProperties, Environment environment, RegistrationId registrationId) { return new NacosRegistration(dataCenterProperties, nacosDiscoveryProperties, environment, registrationId); } @Bean public NacosDiscovery nacosDiscovery( @Qualifier("nacosDiscoveryProperties") NacosDiscoveryProperties nacosDiscoveryProperties) { return new NacosDiscovery(nacosDiscoveryProperties); } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; public class NacosConst { public static final String PROPERTY_VERSION = "scb-version"; public static final String PROPERTY_ALIAS = "scb-alias"; public static final String PROPERTY_DESCRIPTION = "scb-description"; public static final String PROPERTY_ENDPOINT = "scb-endpoint"; public static final String PROPERTY_REGION = "scb-region"; public static final String PROPERTY_ZONE = "scb-zone"; public static final String PROPERTY_DATACENTER = "scb-datacenter"; public static final String PROPERTY_SCHEMA_PREFIX = "scb-schema-"; public static final String ENDPOINT_PROPERTY_SEPARATOR = ","; public static final String NACOS_REGISTRY_PREFIX = "servicecomb.registry.nacos"; public static final String SERVER_ADDR = "serverAddr"; public static final String USERNAME = "username"; public static final String PASSWORD = "password"; public static final String NAMESPACE = "namespace"; public static final String NACOS_NAMING_LOG_NAME = "com.alibaba.nacos.naming.log.filename"; public static final String ACCESS_KEY = "accessKey"; public static final String SECRET_KEY = "secretKey"; public static final String CLUSTER_NAME = "clusterName"; public static final String NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart"; public static final String NACOS_REGISTRY_NAME = "nacos-registry"; public static final String NACOS_STATUS = "nacos-status"; } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import com.alibaba.nacos.api.exception.NacosException; import org.apache.servicecomb.registry.api.Discovery; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.pojo.Instance; public class NacosDiscovery implements Discovery { public static final String NACOS_DISCOVERY_ENABLED = "servicecomb.registry.nacos.%s.%s.enabled"; private static final Map> SUBSCRIBES = new HashMap<>(); private final Object lock = new Object(); private final NacosDiscoveryProperties nacosDiscoveryProperties; private Environment environment; private NamingService namingService; private InstanceChangedListener instanceChangedListener; @Autowired public NacosDiscovery(NacosDiscoveryProperties nacosDiscoveryProperties) { this.nacosDiscoveryProperties = nacosDiscoveryProperties; } @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { this.environment = environment; } @Override public String name() { return NacosConst.NACOS_REGISTRY_NAME; } @Override public boolean enabled(String application, String serviceName) { return environment.getProperty(String.format(NACOS_DISCOVERY_ENABLED, application, serviceName), boolean.class, true); } @Override public List findServiceInstances(String application, String serviceName) { try { // Get the instance first, and the Listener will not be executed for the first subscribe. List instances = namingService.getAllInstances(serviceName, application, true); AtomicBoolean result = SUBSCRIBES.computeIfAbsent(application, k -> new HashMap<>()).computeIfAbsent(serviceName, k -> new AtomicBoolean(true)); if (result.get()) { synchronized (lock) { if (result.get()) { namingService.subscribe(serviceName, application, (event) -> { if (event instanceof NamingEvent) { this.instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(((NamingEvent) event).getInstances(), application, serviceName)); } }); result.set(false); } } } return convertServiceInstanceList(instances, application, serviceName); } catch (Exception e) { throw new IllegalStateException(e); } } @Override public List findServices(String application) { try { return namingService.getServicesOfServer(0, Integer.MAX_VALUE, application) .getData(); } catch (Exception e) { throw new IllegalStateException(e); } } private List convertServiceInstanceList( List instances, String application, String serviceName) { if (CollectionUtils.isEmpty(instances)) { return Collections.emptyList(); } List result = new ArrayList<>(); for (Instance instance : instances) { result.add(new NacosDiscoveryInstance(instance, application, serviceName, environment)); } return result; } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { this.instanceChangedListener = instanceChangedListener; } @Override public void init() { namingService = NamingServiceManager.buildNamingService(environment, nacosDiscoveryProperties); } @Override public void run() { } @Override public void destroy() { if (namingService != null) { try { namingService.shutDown(); } catch (NacosException e) { throw new IllegalStateException("destroy process is interrupted."); } } } @Override public boolean enabled() { return nacosDiscoveryProperties.isEnabled(); } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.springframework.core.env.Environment; import com.alibaba.nacos.api.naming.pojo.Instance; public class NacosDiscoveryInstance extends AbstractDiscoveryInstance { private final Instance instance; private final String application; private final String serviceName; private final Environment environment; private final Map schemas; private final List endpoints; public NacosDiscoveryInstance(Instance instance, String application, String serviceName, Environment environment) { this.instance = instance; this.environment = environment; this.application = application; this.serviceName = serviceName; this.endpoints = readEndpoints(instance); this.schemas = readSchemas(instance); } @Override public MicroserviceInstanceStatus getStatus() { if (instance.isEnabled()) { String instanceStatus = instance.getMetadata().get(NacosConst.NACOS_STATUS); if (StringUtils.isBlank(instanceStatus)) { return MicroserviceInstanceStatus.UP; } return MicroserviceInstanceStatus.valueOf(instanceStatus); } else { return MicroserviceInstanceStatus.DOWN; } } @Override public String getRegistryName() { return NacosConst.NACOS_REGISTRY_NAME; } @Override public String getEnvironment() { return BootStrapProperties.readServiceEnvironment(environment); } @Override public String getApplication() { return this.application; } @Override public String getServiceName() { // nacos instance service name may contain group and `@` annotations return this.serviceName; } @Override public String getAlias() { return instance.getMetadata().get(NacosConst.PROPERTY_ALIAS); } @Override public String getVersion() { return instance.getMetadata().get(NacosConst.PROPERTY_VERSION); } @Override public DataCenterInfo getDataCenterInfo() { DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setRegion(instance.getMetadata().get(NacosConst.PROPERTY_REGION)); dataCenterInfo.setAvailableZone(instance.getMetadata().get(NacosConst.PROPERTY_ZONE)); dataCenterInfo.setName(instance.getMetadata().get(NacosConst.PROPERTY_DATACENTER)); return dataCenterInfo; } @Override public String getDescription() { return instance.getMetadata().get(NacosConst.PROPERTY_DESCRIPTION); } @Override public Map getProperties() { return instance.getMetadata(); } @Override public Map getSchemas() { return schemas; } private static Map readSchemas(Instance instance) { Map metaData = instance.getMetadata(); Map instanceSchemas = new HashMap<>(); for (Map.Entry entry : metaData.entrySet()) { if (entry.getKey().startsWith(NacosConst.PROPERTY_SCHEMA_PREFIX)) { instanceSchemas.put(entry.getKey().substring(NacosConst.PROPERTY_SCHEMA_PREFIX.length()), entry.getValue()); } } return instanceSchemas; } @Override public List getEndpoints() { return endpoints; } private static List readEndpoints(Instance instance) { if (StringUtils.isEmpty(instance.getMetadata().get(NacosConst.PROPERTY_ENDPOINT))) { // interoperate with spring cloud using nacos if (StringUtils.isNotEmpty(instance.getIp()) && instance.getPort() > 0) { return List.of("rest://" + instance.getIp() + ":" + instance.getPort()); } return Collections.emptyList(); } return Arrays.asList(instance.getMetadata().get(NacosConst.PROPERTY_ENDPOINT) .split(NacosConst.ENDPOINT_PROPERTY_SEPARATOR)); } @Override public String getInstanceId() { return instance.getInstanceId(); } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.HashMap; import java.util.Map; public class NacosDiscoveryProperties { private boolean enabled = true; private String serverAddr = "http://127.0.0.1:8848"; private Map metadata = new HashMap<>(); private boolean ephemeral = true; private String username; private String password; private String accessKey; private String secretKey; private String namingLoadCacheAtStart = "false"; private String clusterName = "DEFAULT"; private float weight = 1; private boolean instanceEnabled = true; private String logName; private boolean enableSwaggerRegistration = false; public String getServerAddr() { return serverAddr; } public void setServerAddr(String serverAddr) { this.serverAddr = serverAddr; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } public boolean isEphemeral() { return ephemeral; } public void setEphemeral(boolean ephemeral) { this.ephemeral = ephemeral; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAccessKey() { return accessKey; } public void setAccessKey(String accessKey) { this.accessKey = accessKey; } public String getSecretKey() { return secretKey; } public void setSecretKey(String secretKey) { this.secretKey = secretKey; } public String getNamingLoadCacheAtStart() { return namingLoadCacheAtStart; } public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) { this.namingLoadCacheAtStart = namingLoadCacheAtStart; } public String getClusterName() { return clusterName; } public void setClusterName(String clusterName) { this.clusterName = clusterName; } public float getWeight() { return weight; } public void setWeight(float weight) { this.weight = weight; } public boolean isInstanceEnabled() { return instanceEnabled; } public void setInstanceEnabled(boolean instanceEnabled) { this.instanceEnabled = instanceEnabled; } public String getLogName() { return logName; } public void setLogName(String logName) { this.logName = logName; } public boolean isEnableSwaggerRegistration() { return enableSwaggerRegistration; } public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { this.enableSwaggerRegistration = enableSwaggerRegistration; } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.config.DataCenterProperties; import org.springframework.core.env.Environment; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; public class NacosMicroserviceHandler { private static final String VERSION_MAPPING = "VERSION_MAPPING"; private static final String CAS_APPLICATION_ID = "CAS_APPLICATION_ID"; private static final String CAS_COMPONENT_NAME = "CAS_COMPONENT_NAME"; private static final String CAS_INSTANCE_VERSION = "CAS_INSTANCE_VERSION"; private static final String CAS_INSTANCE_ID = "CAS_INSTANCE_ID"; private static final String CAS_ENVIRONMENT_ID = "CAS_ENVIRONMENT_ID"; private static final String INSTANCE_PROPS = "SERVICECOMB_INSTANCE_PROPS"; public static Instance createMicroserviceInstance( DataCenterProperties dataCenterProperties, NacosDiscoveryProperties properties, Environment environment) { Instance instance = new Instance(); instance.setServiceName(NamingUtils.getGroupedName( BootStrapProperties.readServiceName(environment), BootStrapProperties.readApplication(environment))); instance.setWeight(properties.getWeight()); instance.setEnabled(properties.isInstanceEnabled()); instance.setClusterName(properties.getClusterName()); instance.setEphemeral(properties.isEphemeral()); Map metadata = properties.getMetadata(); metadata.put(NacosConst.PROPERTY_VERSION, BootStrapProperties.readServiceVersion(environment)); metadata.put(NacosConst.PROPERTY_ALIAS, BootStrapProperties.readServiceAlias(environment)); metadata.put(NacosConst.PROPERTY_DESCRIPTION, BootStrapProperties.readServiceDescription(environment)); metadata.put(NacosConst.PROPERTY_DATACENTER, dataCenterProperties.getName()); metadata.put(NacosConst.PROPERTY_REGION, dataCenterProperties.getRegion()); metadata.put(NacosConst.PROPERTY_ZONE, dataCenterProperties.getAvailableZone()); if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { metadata.put("version", environment.getProperty(environment.getProperty(VERSION_MAPPING))); } metadata.putAll(genCasProperties(environment)); instance.setMetadata(metadata); return instance; } private static Map genCasProperties(Environment environment) { Map properties = new HashMap<>(); if (!StringUtils.isEmpty(environment.getProperty(CAS_APPLICATION_ID))) { properties.put(CAS_APPLICATION_ID, environment.getProperty(CAS_APPLICATION_ID)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_COMPONENT_NAME))) { properties.put(CAS_COMPONENT_NAME, environment.getProperty(CAS_COMPONENT_NAME)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_VERSION))) { properties.put(CAS_INSTANCE_VERSION, environment.getProperty(CAS_INSTANCE_VERSION)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_ID))) { properties.put(CAS_INSTANCE_ID, environment.getProperty(CAS_INSTANCE_ID)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_ENVIRONMENT_ID))) { properties.put(CAS_ENVIRONMENT_ID, environment.getProperty(CAS_ENVIRONMENT_ID)); } String[] instancePropArray = ConfigUtil.parseArrayValue(environment.getProperty(INSTANCE_PROPS)) .toArray(new String[0]); if (instancePropArray.length != 0) { properties.putAll(parseProps(instancePropArray)); } return properties; } private static Map parseProps(String... value) { return Arrays.stream(value).map(v -> v.split(":")) .filter(v -> v.length == 2) .collect(Collectors.toMap(v -> v[0], v -> v[1])); } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; public class NacosRegistration implements Registration { private final NacosDiscoveryProperties nacosDiscoveryProperties; private final Environment environment; private final String instanceId; private final DataCenterProperties dataCenterProperties; private NacosRegistrationInstance nacosRegistrationInstance; private Instance instance; private NamingService namingService; @Autowired public NacosRegistration(DataCenterProperties dataCenterProperties, NacosDiscoveryProperties nacosDiscoveryProperties, Environment environment, RegistrationId registrationId) { this.instanceId = registrationId.getInstanceId(); this.dataCenterProperties = dataCenterProperties; this.nacosDiscoveryProperties = nacosDiscoveryProperties; this.environment = environment; } @Override public void init() { instance = NacosMicroserviceHandler.createMicroserviceInstance(dataCenterProperties, nacosDiscoveryProperties, environment); instance.setInstanceId(instanceId); nacosRegistrationInstance = new NacosRegistrationInstance(instance, environment); instance.getMetadata() .put(NacosConst.NACOS_STATUS, BootStrapProperties.readServiceInstanceInitialStatus(environment)); namingService = NamingServiceManager.buildNamingService(environment, nacosDiscoveryProperties); } @Override public void run() { try { if (nacosDiscoveryProperties.isEnableSwaggerRegistration()) { addSchemas(nacosRegistrationInstance.getSchemas(), instance); } addEndpoints(nacosRegistrationInstance.getEndpoints(), instance); namingService.registerInstance(nacosRegistrationInstance.getServiceName(), nacosRegistrationInstance.getApplication(), instance); } catch (NacosException e) { throw new IllegalStateException(e); } } private static void addSchemas(Map schemas, Instance instance) { if (CollectionUtils.isEmpty(schemas)) { return; } for (Map.Entry entry : schemas.entrySet()) { instance.addMetadata(NacosConst.PROPERTY_SCHEMA_PREFIX + entry.getKey(), entry.getValue()); } } private static void addEndpoints(List endpoints, Instance instance) { if (endpoints.isEmpty()) { return; } for (String endpoint : endpoints) { Endpoint temp = EndpointUtils.parse(endpoint); if (temp.getAddress() instanceof URIEndpointObject) { instance.setIp(((URIEndpointObject) temp.getAddress()).getHostOrIp()); instance.setPort(((URIEndpointObject) temp.getAddress()).getPort()); break; } } instance.addMetadata(NacosConst.PROPERTY_ENDPOINT, String.join(NacosConst.ENDPOINT_PROPERTY_SEPARATOR, endpoints)); } @Override public void destroy() { try { namingService.deregisterInstance(nacosRegistrationInstance.getServiceName(), nacosRegistrationInstance.getApplication(), instance); namingService.shutDown(); } catch (NacosException e) { throw new IllegalStateException("destroy process is interrupted."); } } @Override public String name() { return NacosConst.NACOS_REGISTRY_NAME; } @Override public NacosRegistrationInstance getMicroserviceInstance() { return this.nacosRegistrationInstance; } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { try { instance.getMetadata().put(NacosConst.NACOS_STATUS, status.name()); namingService.registerInstance(nacosRegistrationInstance.getServiceName(), nacosRegistrationInstance.getApplication(), instance); return true; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void addSchema(String schemaId, String content) { if (nacosDiscoveryProperties.isEnableSwaggerRegistration()) { nacosRegistrationInstance.addSchema(schemaId, content); } } @Override public void addEndpoint(String endpoint) { nacosRegistrationInstance.addEndpoint(endpoint); } @Override public void addProperty(String key, String value) { instance.addMetadata(key, value); } @Override public boolean enabled() { return nacosDiscoveryProperties.isEnabled(); } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.springframework.core.env.Environment; import com.alibaba.nacos.api.naming.pojo.Instance; public class NacosRegistrationInstance implements RegistrationInstance { private final Instance instance; private final Map schemas = new HashMap<>(); private final List endpoints = new ArrayList<>(); private final Environment environment; public NacosRegistrationInstance(Instance instance, Environment environment) { this.instance = instance; this.environment = environment; } @Override public String getEnvironment() { return BootStrapProperties.readServiceEnvironment(environment); } @Override public String getApplication() { return BootStrapProperties.readApplication(environment); } @Override public String getServiceName() { return BootStrapProperties.readServiceName(environment); } @Override public String getAlias() { return BootStrapProperties.readServiceAlias(environment); } @Override public String getVersion() { return instance.getMetadata().get(NacosConst.PROPERTY_VERSION); } @Override public DataCenterInfo getDataCenterInfo() { DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setRegion(instance.getMetadata().get(NacosConst.PROPERTY_REGION)); dataCenterInfo.setAvailableZone(instance.getMetadata().get(NacosConst.PROPERTY_ZONE)); return dataCenterInfo; } @Override public String getDescription() { return BootStrapProperties.readServiceDescription(environment); } @Override public Map getProperties() { return instance.getMetadata(); } @Override public Map getSchemas() { return schemas; } @Override public List getEndpoints() { return endpoints; } @Override public String getInstanceId() { return instance.getInstanceId(); } @Override public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.valueOf(instance.getMetadata().get(NacosConst.NACOS_STATUS)); } public void addSchema(String schemaId, String content) { this.schemas.put(schemaId, content); } public void addEndpoint(String endpoint) { this.endpoints.add(endpoint); } } ================================================ FILE: service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NamingServiceManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.nacos; import java.util.Objects; import java.util.Properties; import org.apache.servicecomb.config.BootStrapProperties; import org.springframework.core.env.Environment; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.client.naming.NacosNamingService; public class NamingServiceManager { private static volatile NamingService namingService; public static NamingService buildNamingService(Environment environment, NacosDiscoveryProperties properties) { if (Objects.isNull(namingService)) { synchronized (NamingServiceManager.class) { if (Objects.isNull(namingService)) { try { namingService = new NacosNamingService(getProperties(environment, properties)); } catch (NacosException e) { throw new IllegalStateException("build namingService failed."); } } } } return namingService; } private static Properties getProperties(Environment environment, NacosDiscoveryProperties nacosDiscoveryProperties) { Properties properties = new Properties(); properties.put(NacosConst.NAMESPACE, BootStrapProperties.readServiceEnvironment(environment)); properties.put(NacosConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr()); if (nacosDiscoveryProperties.getUsername() != null) { properties.put(NacosConst.USERNAME, nacosDiscoveryProperties.getUsername()); } if (nacosDiscoveryProperties.getPassword() != null) { properties.put(NacosConst.PASSWORD, nacosDiscoveryProperties.getPassword()); } if (nacosDiscoveryProperties.getAccessKey() != null) { properties.put(NacosConst.ACCESS_KEY, nacosDiscoveryProperties.getAccessKey()); } if (nacosDiscoveryProperties.getSecretKey() != null) { properties.put(NacosConst.SECRET_KEY, nacosDiscoveryProperties.getSecretKey()); } if (nacosDiscoveryProperties.getLogName() != null) { properties.put(NacosConst.NACOS_NAMING_LOG_NAME, nacosDiscoveryProperties.getLogName()); } properties.put(NacosConst.CLUSTER_NAME, nacosDiscoveryProperties.getClusterName()); properties.put(NacosConst.NAMING_LOAD_CACHE_AT_START, nacosDiscoveryProperties.getNamingLoadCacheAtStart()); return properties; } } ================================================ FILE: service-registry/registry-nacos/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.nacos.NacosConfiguration ================================================ FILE: service-registry/registry-service-center/pom.xml ================================================ org.apache.servicecomb service-registry-parent 3.4.0-SNAPSHOT 4.0.0 registry-service-center Java Chassis::Service Registry::Service Center org.apache.servicecomb foundation-vertx org.apache.servicecomb foundation-config org.apache.servicecomb foundation-common org.apache.servicecomb swagger-generator-core org.apache.servicecomb java-chassis-core org.apache.servicecomb service-center-client org.apache.commons commons-lang3 io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java ================================================ /* * Copyright (C) 2020-2022 Huawei Technologies Co., Ltd. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.service.center.client.model.DataCenterInfo; import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HealthCheck; import org.apache.servicecomb.service.center.client.model.HealthCheckMode; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; import org.apache.servicecomb.service.center.client.model.MicroserviceStatus; import org.springframework.core.env.Environment; public class MicroserviceHandler { private static final String SERVICE_MAPPING = "SERVICE_MAPPING"; private static final String VERSION_MAPPING = "VERSION_MAPPING"; private static final String APP_MAPPING = "APP_MAPPING"; private static final String CAS_APPLICATION_ID = "CAS_APPLICATION_ID"; private static final String CAS_COMPONENT_NAME = "CAS_COMPONENT_NAME"; private static final String CAS_INSTANCE_VERSION = "CAS_INSTANCE_VERSION"; private static final String CAS_INSTANCE_ID = "CAS_INSTANCE_ID"; private static final String CAS_ENVIRONMENT_ID = "CAS_ENVIRONMENT_ID"; private static final String SERVICE_PROPS = "SERVICECOMB_SERVICE_PROPS"; private static final String INSTANCE_PROPS = "SERVICECOMB_INSTANCE_PROPS"; public static Microservice createMicroservice( Environment environment) { Microservice microservice = new Microservice(); microservice.setProperties(BootStrapProperties.readServiceProperties(environment)); if (!StringUtils.isEmpty(environment.getProperty(APP_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(APP_MAPPING)))) { microservice.setAppId(environment.getProperty(environment.getProperty(APP_MAPPING))); } else { microservice.setAppId(BootStrapProperties.readApplication(environment)); } if (!StringUtils.isEmpty(environment.getProperty(SERVICE_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(SERVICE_MAPPING)))) { microservice.setServiceName(environment.getProperty(environment.getProperty(SERVICE_MAPPING))); } else { microservice.setServiceName(BootStrapProperties.readServiceName(environment)); } if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { microservice.setVersion(environment.getProperty(environment.getProperty(VERSION_MAPPING))); } else { microservice.setVersion(BootStrapProperties.readServiceVersion(environment)); } microservice.setEnvironment(BootStrapProperties.readServiceEnvironment(environment)); Framework framework = createFramework(); microservice.setFramework(framework); String[] servicePropArray = ConfigUtil.parseArrayValue(environment.getProperty(SERVICE_PROPS)) .toArray(new String[0]); if (servicePropArray.length != 0) { microservice.getProperties().putAll(parseProps(servicePropArray)); } microservice.setStatus(MicroserviceStatus.UP); return microservice; } private static Framework createFramework() { Framework framework = new Framework(); framework.setName("ServiceComb"); framework.setVersion(MicroserviceHandler.class.getPackage().getImplementationVersion()); return framework; } public static MicroserviceInstance createMicroserviceInstance( Environment environment, SCConfigurationProperties scConfigurationProperties, DataCenterProperties dataCenterProperties) { MicroserviceInstance microserviceInstance = new MicroserviceInstance(); String hostName = StringUtils.isEmpty(scConfigurationProperties.getHostname()) ? NetUtils.getHostName() : scConfigurationProperties.getHostname(); if (hostName.length() > 64) { hostName = hostName.substring(0, 64); } microserviceInstance.setHostName(hostName); if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setName(dataCenterProperties.getName()); dataCenterInfo.setRegion(dataCenterProperties.getRegion()); dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); microserviceInstance.setDataCenterInfo(dataCenterInfo); } HealthCheck healthCheck = new HealthCheck(); healthCheck.setMode(HealthCheckMode.push); healthCheck.setInterval(scConfigurationProperties.getHealthCheckIntervalInSeconds()); healthCheck.setTimes(scConfigurationProperties.getHealthCheckTimes()); microserviceInstance.setHealthCheck(healthCheck); String currTime = String.valueOf(System.currentTimeMillis()); microserviceInstance.setTimestamp(currTime); microserviceInstance.setModTimestamp(currTime); // what's MicroserviceInstance doing? same sa Microservice? if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { microserviceInstance.setVersion(environment.getProperty(environment.getProperty(VERSION_MAPPING))); } else { microserviceInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); } Map properties = new HashMap<>(); properties.putAll(BootStrapProperties.readServiceProperties(environment)); properties.putAll(genCasProperties(environment)); microserviceInstance.setProperties(properties); microserviceInstance.setStatus( MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); return microserviceInstance; } private static Map genCasProperties(Environment environment) { Map properties = new HashMap<>(); if (!StringUtils.isEmpty(environment.getProperty(CAS_APPLICATION_ID))) { properties.put(CAS_APPLICATION_ID, environment.getProperty(CAS_APPLICATION_ID)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_COMPONENT_NAME))) { properties.put(CAS_COMPONENT_NAME, environment.getProperty(CAS_COMPONENT_NAME)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_VERSION))) { properties.put(CAS_INSTANCE_VERSION, environment.getProperty(CAS_INSTANCE_VERSION)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_ID))) { properties.put(CAS_INSTANCE_ID, environment.getProperty(CAS_INSTANCE_ID)); } if (!StringUtils.isEmpty(environment.getProperty(CAS_ENVIRONMENT_ID))) { properties.put(CAS_ENVIRONMENT_ID, environment.getProperty(CAS_ENVIRONMENT_ID)); } String[] instancePropArray = ConfigUtil.parseArrayValue(environment.getProperty(INSTANCE_PROPS)) .toArray(new String[0]); if (instancePropArray.length != 0) { properties.putAll(parseProps(instancePropArray)); } return properties; } private static Map parseProps(String... value) { return Arrays.stream(value).map(v -> v.split(":")) .filter(v -> v.length == 2) .collect(Collectors.toMap(v -> v[0], v -> v[1])); } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.apache.servicecomb.service.center.client.RegistrationEvents.HeartBeatEvent; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.DataCenterInfo; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; /** * Support auto discovery of service center addresses. */ public class SCAddressManager { public enum Type { SERVICECENTER, KIE, CseConfigCenter, } private static final Logger LOGGER = LoggerFactory.getLogger(SCAddressManager.class); private final ServiceCenterClient serviceCenterClient; private final SCRegistration scRegistration; private final SCConfigurationProperties configurationProperties; private final Map> lastEngineEndpointsCache = new HashMap<>(); public SCAddressManager(SCConfigurationProperties configurationProperties, ServiceCenterClient serviceCenterClient, SCRegistration scRegistration) { this.configurationProperties = configurationProperties; this.serviceCenterClient = serviceCenterClient; this.scRegistration = scRegistration; scRegistration.getEventBus().register(this); } @Subscribe public void onHeartBeatEvent(HeartBeatEvent event) { if (event.isSuccess() && configurationProperties.isAutoDiscovery()) { for (Type type : Type.values()) { initEndPort(type.name()); } } } private void initEndPort(String key) { List instances = findServiceInstance("default", key, "0+"); HashSet currentEngineEndpoints = new HashSet<>(); Map> zoneAndRegion = generateZoneAndRegionAddress(instances, currentEngineEndpoints); if (zoneAndRegion == null) { return; } if (isEngineEndpointsChanged(lastEngineEndpointsCache.get(key), currentEngineEndpoints)) { LOGGER.info("auto discovery service [{}] addresses: [{}]", key, zoneAndRegion); lastEngineEndpointsCache.put(key, currentEngineEndpoints); EventManager.post(new RefreshEndpointEvent(zoneAndRegion, key)); } } private boolean isEngineEndpointsChanged(Set lastEngineEndpoints, Set currentEngineEndpoints) { if (lastEngineEndpoints == null || lastEngineEndpoints.isEmpty()) { return true; } HashSet compareTemp = new HashSet<>(lastEngineEndpoints); compareTemp.removeAll(currentEngineEndpoints); return !compareTemp.isEmpty() || lastEngineEndpoints.size() != currentEngineEndpoints.size(); } private Map> generateZoneAndRegionAddress(List instances, HashSet currentEngineEndpoints) { if (instances.isEmpty()) { return null; } Map> zoneAndRegion = new HashMap<>(); DataCenterInfo dataCenterInfo = findRegion(instances); Set sameZone = new HashSet<>(); Set sameRegion = new HashSet<>(); for (MicroserviceInstance microserviceInstance : instances) { if (regionAndAZMatch(dataCenterInfo, microserviceInstance)) { sameZone.addAll(microserviceInstance.getEndpoints()); } else { sameRegion.addAll(microserviceInstance.getEndpoints()); } currentEngineEndpoints.addAll(microserviceInstance.getEndpoints()); } zoneAndRegion.put("sameZone", new ArrayList<>(sameZone)); zoneAndRegion.put("sameRegion", new ArrayList<>(sameRegion)); return zoneAndRegion; } private DataCenterInfo findRegion(List microserviceInstances) { for (MicroserviceInstance microserviceInstance : microserviceInstances) { boolean isMatch = microserviceInstance.getEndpoints().get(0) .contains(scRegistration.getBackendMicroserviceInstance() .getEndpoints().get(0)); if (isMatch && microserviceInstance.getDataCenterInfo() != null) { return microserviceInstance.getDataCenterInfo(); } } if (scRegistration.getBackendMicroserviceInstance().getDataCenterInfo() == null) { return null; } return scRegistration.getBackendMicroserviceInstance().getDataCenterInfo(); } private List findServiceInstance(String appId, String serviceName, String versionRule) { try { FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient .findMicroserviceInstance(scRegistration.getBackendMicroserviceInstance().getServiceId(), appId, serviceName, versionRule, null); return instancesResponse.getMicroserviceInstancesResponse().getInstances(); } catch (OperationException operationException) { LOGGER.warn("not find the Microservice instance of {}", serviceName); return new ArrayList<>(); } } private boolean regionAndAZMatch(DataCenterInfo myself, MicroserviceInstance target) { if (myself == null) { return true; } if (target.getDataCenterInfo() != null) { return myself.getRegion().equals(target.getDataCenterInfo().getRegion()) && myself.getAvailableZone().equals(target.getDataCenterInfo().getAvailableZone()); } return false; } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; import org.apache.servicecomb.foundation.vertx.VertxConst; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterRawClient; import org.apache.servicecomb.service.center.client.ServiceCenterWatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; public class SCClientUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SCClientUtils.class); // Compatible chassis multi-registration center private static final Map serviceAddressManagers = new ConcurrentHashMap<>(); public static ServiceCenterAddressManager createAddressManager(SCConfigurationProperties discoveryProperties, Environment environment) { List addresses = ConfigUtil.parseArrayValue(discoveryProperties.getAddress()); return createAddressManager("default", addresses, environment); } /** * Ensure that the ServiceCenterAddressManager in the client created for RBAC authentication and registry discovery * is the same. This ensures that when an error is reported due to the registry center address being unavailable, * the authentication and registry discovery remain consistent. * * @param projectName projectName * @param addresses engine address * @param environment environment * @return Service Center Address Manager */ public static ServiceCenterAddressManager createAddressManager(String projectName, List addresses, Environment environment) { if (getServiceCenterAddressManager(addresses) == null) { synchronized (SCClientUtils.class) { if (getServiceCenterAddressManager(addresses) == null) { String key = String.join(",", addresses); LOGGER.info("initialize discovery server={}", addresses); String region = environment.getProperty("servicecomb.datacenter.region"); String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(projectName, addresses, EventManager.getEventBus(), region, availableZone); serviceAddressManagers.put(key, addressManager); return addressManager; } } } return getServiceCenterAddressManager(addresses); } private static ServiceCenterAddressManager getServiceCenterAddressManager(List addresses) { String forwardKey = String.join(",", addresses); List tempAddr = new ArrayList<>(addresses); Collections.reverse(tempAddr); String reverseKey = String.join(",", tempAddr); if (serviceAddressManagers.get(forwardKey) != null) { return serviceAddressManagers.get(forwardKey); } if (serviceAddressManagers.get(reverseKey) != null) { return serviceAddressManagers.get(reverseKey); } return null; } // add other headers needed for registration by new ServiceCenterClient(...) public static ServiceCenterClient serviceCenterClient(SCConfigurationProperties discoveryProperties, Environment environment) { ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties, environment); SSLProperties sslProperties = buildSslProperties(addressManager, environment); return new ServiceCenterClient(new ServiceCenterRawClient.Builder() .setTenantName("default") .setAddressManager(addressManager) .setHttpTransport(createHttpTransport(environment, sslProperties)).build(), addressManager); } private static HttpTransport createHttpTransport(Environment environment, SSLProperties sslProperties) { List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); if (isProxyEnable(environment)) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). setDefaultRequestConfig(HttpTransportFactory.defaultRequestConfig().build()); HttpHost proxy = new HttpHost(getProxyHost(environment), getProxyPort(environment), "http"); // now only support http proxy httpClientBuilder.setProxy(proxy); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(getProxyUsername(environment), getProxyPasswd(environment))); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return HttpTransportFactory .createHttpTransport(sslProperties, getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); } return HttpTransportFactory .createHttpTransport(sslProperties, getRequestAuthHeaderProvider(authHeaderProviders), HttpTransportFactory.defaultRequestConfig().build()); } public static Boolean isProxyEnable(Environment environment) { return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); } public static String getProxyHost(Environment environment) { return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); } public static int getProxyPort(Environment environment) { return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); } public static String getProxyUsername(Environment environment) { return environment.getProperty(VertxConst.PROXY_USERNAME); } public static String getProxyPasswd(Environment environment) { return environment.getProperty(VertxConst.PROXY_PASSWD); } private static SSLProperties buildSslProperties(ServiceCenterAddressManager addressManager, Environment environment) { SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(SCConst.SC_SSL_TAG, environment); SSLOption sslOption; if (factory == null) { sslOption = SSLOption.build(SCConst.SC_SSL_TAG, environment); } else { sslOption = factory.createSSLOption(); } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); SSLProperties sslProperties = new SSLProperties(); sslProperties.setSslCustom(sslCustom); sslProperties.setSslOption(sslOption); sslProperties.setEnabled(addressManager.sslEnabled()); return sslProperties; } public static ServiceCenterWatch serviceCenterWatch(SCConfigurationProperties discoveryProperties, List authHeaderProviders, Environment environment) { ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties, environment); SSLProperties sslProperties = buildSslProperties(addressManager, environment); return new ServiceCenterWatch(addressManager, sslProperties, getRequestAuthHeaderProvider(authHeaderProviders), "default", new HashMap<>(), EventManager.getEventBus()); } private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.List; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterWatch; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration @SuppressWarnings("unused") public class SCConfiguration { @Bean @ConfigurationProperties(prefix = SCConst.SC_REGISTRY_PREFIX) public SCConfigurationProperties scConfigurationProperties() { return new SCConfigurationProperties(); } @Bean public ServiceCenterClient serviceCenterClient( @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, Environment environment) { return SCClientUtils.serviceCenterClient(scConfigurationProperties, environment); } @Bean public ServiceCenterWatch serviceCenterWatch( @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, List authHeaderProviders, Environment environment) { return SCClientUtils.serviceCenterWatch(scConfigurationProperties, authHeaderProviders, environment); } @Bean public SCRegistration scRegistration( @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, @Qualifier("serviceCenterClient") ServiceCenterClient serviceCenterClient, @Qualifier("serviceCenterWatch") ServiceCenterWatch serviceCenterWatch) { return new SCRegistration(scConfigurationProperties, serviceCenterClient, serviceCenterWatch); } @Bean public SCDiscovery scDiscovery( @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, @Qualifier("serviceCenterClient") ServiceCenterClient serviceCenterClient) { return new SCDiscovery(scConfigurationProperties, serviceCenterClient); } @Bean public SCAddressManager scAddressManager( @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, SCRegistration scRegistration, @Qualifier("serviceCenterClient") ServiceCenterClient serviceCenterClient) { return new SCAddressManager(scConfigurationProperties, serviceCenterClient, scRegistration); } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import org.apache.commons.lang3.StringUtils; /** * Configurations for Service Center registration and discovery. */ public class SCConfigurationProperties { private boolean enabled = true; private String address = null; private boolean enableSwaggerRegistration = false; /** * for registration service * when swagger is different between local with remote serviceCenter. if ignoreSwaggerDifferent is true. * it will ignore the different and continue the program. otherwise, the program will stop. */ private boolean ignoreSwaggerDifferent = true; private boolean canOverwriteSwagger = true; private String hostname; private int healthCheckIntervalInSeconds = 15; private int healthCheckTimes = 3; private int healthCheckRequestTimeoutInMillis = 5000; private int pollIntervalInMillis = 15000; private boolean autoDiscovery = false; private boolean watch = false; private long registrationWaitTimeInMillis = 30000; public String getAddress() { if (StringUtils.isEmpty(address)) { throw new IllegalStateException( "Address is required in configuration. NOTICE: since 3.0.0, only support " + SCConst.SC_REGISTRY_PREFIX + ".address to configure service center address."); } return address; } public void setAddress(String address) { this.address = address; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isIgnoreSwaggerDifferent() { return ignoreSwaggerDifferent; } public void setIgnoreSwaggerDifferent(boolean ignoreSwaggerDifferent) { this.ignoreSwaggerDifferent = ignoreSwaggerDifferent; } public boolean isCanOverwriteSwagger() { return canOverwriteSwagger; } public void setCanOverwriteSwagger(boolean canOverwriteSwagger) { this.canOverwriteSwagger = canOverwriteSwagger; } public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public int getHealthCheckIntervalInSeconds() { return healthCheckIntervalInSeconds; } public void setHealthCheckIntervalInSeconds(int healthCheckIntervalInSeconds) { this.healthCheckIntervalInSeconds = healthCheckIntervalInSeconds; } public int getHealthCheckRequestTimeoutInMillis() { return healthCheckRequestTimeoutInMillis; } public void setHealthCheckRequestTimeoutInMillis(int healthCheckRequestTimeoutInMillis) { this.healthCheckRequestTimeoutInMillis = healthCheckRequestTimeoutInMillis; } public int getPollIntervalInMillis() { return pollIntervalInMillis; } public void setPollIntervalInMillis(int pollIntervalInMillis) { this.pollIntervalInMillis = pollIntervalInMillis; } public boolean isAutoDiscovery() { return autoDiscovery; } public void setAutoDiscovery(boolean autoDiscovery) { this.autoDiscovery = autoDiscovery; } public int getHealthCheckTimes() { return healthCheckTimes; } public void setHealthCheckTimes(int healthCheckTimes) { this.healthCheckTimes = healthCheckTimes; } public boolean isWatch() { return watch; } public void setWatch(boolean watch) { this.watch = watch; } public long getRegistrationWaitTimeInMillis() { return registrationWaitTimeInMillis; } public void setRegistrationWaitTimeInMillis(long registrationWaitTimeInMillis) { this.registrationWaitTimeInMillis = registrationWaitTimeInMillis; } public boolean isEnableSwaggerRegistration() { return enableSwaggerRegistration; } public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { this.enableSwaggerRegistration = enableSwaggerRegistration; } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; public class SCConst { public static final String SC_REGISTRY_NAME = "sc-registry"; public static final String SC_REGISTRY_PREFIX = "servicecomb.registry.sc"; public static final String SC_SSL_TAG = "sc.consumer"; public static final String SC_ALLOW_CROSS_APP = "allowCrossApp"; public static final String SC_DEFAULT_PROJECT = "default"; } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.service.center.client.DiscoveryEvents.InstanceChangedEvent; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery; import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery.SubscriptionKey; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class SCDiscovery implements Discovery { public static final String SC_DISCOVERY_ENABLED = "servicecomb.registry.sc.%s.%s.enabled"; private final EventBus eventBus = new SimpleEventBus(); private final SCConfigurationProperties configurationProperties; private final ServiceCenterClient serviceCenterClient; private SCRegistration scRegistration; private ServiceCenterDiscovery serviceCenterDiscovery; private InstanceChangedListener instanceChangedListener; private Environment environment; @Autowired public SCDiscovery(SCConfigurationProperties configurationProperties, ServiceCenterClient serviceCenterClient) { this.configurationProperties = configurationProperties; this.serviceCenterClient = serviceCenterClient; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setScRegistration(SCRegistration scRegistration) { this.scRegistration = scRegistration; } @Override public String name() { return SCConst.SC_REGISTRY_NAME; } @Override public int getOrder() { return -9000; } @Override public boolean enabled(String application, String serviceName) { return environment.getProperty(String.format(SC_DISCOVERY_ENABLED, application, serviceName), boolean.class, true); } @Override public List findServiceInstances(String application, String serviceName) { SubscriptionKey subscriptionKey = new SubscriptionKey(application, serviceName); serviceCenterDiscovery.registerIfNotPresent(subscriptionKey); List instances = serviceCenterDiscovery.getInstanceCache(subscriptionKey); return toDiscoveryInstances(instances); } @Override public List findServices(String application) { return serviceCenterClient.getMicroserviceList().getServices().stream() .filter(e -> e.getAppId().equals(application)).map(Microservice::getServiceName) .collect(Collectors.toList()); } private List toDiscoveryInstances(List instances) { if (CollectionUtils.isEmpty(instances)) { return Collections.emptyList(); } Map schemaResult; if (configurationProperties.isEnableSwaggerRegistration()) { List schemas = serviceCenterClient.getServiceSchemasList(instances.get(0).getServiceId(), true); if (schemas == null) { schemaResult = Collections.emptyMap(); } else { schemaResult = new HashMap<>(schemas.size()); schemas.forEach(info -> schemaResult.put(info.getSchemaId(), info.getSchema())); } } else { schemaResult = new HashMap<>(); } List result = new ArrayList<>(instances.size()); instances.forEach(instance -> result.add(new SCDiscoveryInstance(instance, schemaResult))); return result; } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { this.instanceChangedListener = instanceChangedListener; } @Subscribe @SuppressWarnings("unused") public void onInstanceChangedEvent(InstanceChangedEvent event) { this.instanceChangedListener.onInstanceChanged(name(), event.getAppName(), event.getServiceName(), toDiscoveryInstances(event.getInstances())); } @Override public void init() { serviceCenterDiscovery = new ServiceCenterDiscovery(serviceCenterClient, eventBus); serviceCenterDiscovery.setPollInterval(configurationProperties.getPollIntervalInMillis()); eventBus.register(this); } @Override public void run() { // SCDiscovery.run is called after SCRegistration.run serviceCenterDiscovery.updateMyselfServiceId(scRegistration.getBackendMicroservice().getServiceId()); // startDiscovery will check if already started, can call several times serviceCenterDiscovery.startDiscovery(); } @Override public void destroy() { if (serviceCenterDiscovery != null) { serviceCenterDiscovery.stop(); } } @Override public boolean enabled() { return this.configurationProperties.isEnabled(); } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; public class SCDiscoveryInstance extends AbstractDiscoveryInstance { private final Microservice microservice; private final MicroserviceInstance microserviceInstance; private final Map schemas; public SCDiscoveryInstance(MicroserviceInstance microserviceInstance, Map schemas) { this.microservice = microserviceInstance.getMicroservice(); this.microserviceInstance = microserviceInstance; this.schemas = schemas; } @Override public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.valueOf(microserviceInstance.getStatus().name()); } @Override public String getRegistryName() { return SCConst.SC_REGISTRY_NAME; } @Override public String getEnvironment() { return microservice.getEnvironment(); } @Override public String getApplication() { return microservice.getAppId(); } @Override public String getServiceName() { return microservice.getServiceName(); } @Override public String getAlias() { return microservice.getAlias(); } @Override public String getVersion() { return microservice.getVersion(); } @Override public DataCenterInfo getDataCenterInfo() { if (microserviceInstance.getDataCenterInfo() != null) { return new DataCenterInfo(microserviceInstance.getDataCenterInfo().getName(), microserviceInstance.getDataCenterInfo().getRegion(), microserviceInstance.getDataCenterInfo().getAvailableZone()); } return new DataCenterInfo(); } @Override public String getDescription() { return microservice.getDescription(); } @Override public Map getProperties() { return microserviceInstance.getProperties(); } @Override public Map getSchemas() { return schemas; } @Override public List getEndpoints() { return microserviceInstance.getEndpoints(); } @Override public String getInstanceId() { return microserviceInstance.getInstanceId(); } @Override public String getServiceId() { return microservice.getServiceId(); } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterRegistration; import org.apache.servicecomb.service.center.client.ServiceCenterWatch; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.apache.servicecomb.service.center.client.model.ServiceCenterConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.base.Charsets; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.common.hash.Hashing; public class SCRegistration implements Registration { private final EventBus eventBus = new SimpleEventBus(); private Microservice microservice; private MicroserviceInstance microserviceInstance; private ServiceCenterRegistration serviceCenterRegistration; private final ServiceCenterClient serviceCenterClient; private final ServiceCenterWatch serviceCenterWatch; private final SCConfigurationProperties configurationProperties; private SCRegistrationInstance registrationInstance; private DataCenterProperties dataCenterProperties; private Environment environment; private RegistrationId registrationId; private CountDownLatch readyWaiter = new CountDownLatch(1); @Autowired public SCRegistration(SCConfigurationProperties configurationProperties, ServiceCenterClient serviceCenterClient, ServiceCenterWatch serviceCenterWatch) { this.configurationProperties = configurationProperties; this.serviceCenterClient = serviceCenterClient; this.serviceCenterWatch = serviceCenterWatch; } @Autowired public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { this.dataCenterProperties = dataCenterProperties; } @Autowired public void setRegistrationId(RegistrationId registrationId) { this.registrationId = registrationId; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void init() { microservice = MicroserviceHandler.createMicroservice( environment); microserviceInstance = MicroserviceHandler.createMicroserviceInstance( environment, configurationProperties, dataCenterProperties); microserviceInstance.setInstanceId(registrationId.getInstanceId()); serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, new ServiceCenterConfiguration().setCanOverwriteSwagger( this.configurationProperties.isCanOverwriteSwagger()) .setCanOverwriteSwagger(this.configurationProperties.isCanOverwriteSwagger()), eventBus); serviceCenterRegistration.setMicroservice(microservice); serviceCenterRegistration.setMicroserviceInstance(microserviceInstance); serviceCenterRegistration.setHeartBeatInterval( TimeUnit.SECONDS.toMillis(configurationProperties.getHealthCheckIntervalInSeconds())); serviceCenterRegistration.setHeartBeatRequestTimeout( configurationProperties.getHealthCheckRequestTimeoutInMillis()); registrationInstance = new SCRegistrationInstance(microservice, microserviceInstance, serviceCenterRegistration); eventBus.register(this); } @Override public void run() { try { serviceCenterRegistration.startRegistration(); if (!readyWaiter.await(configurationProperties.getRegistrationWaitTimeInMillis(), TimeUnit.MILLISECONDS)) { throw new IllegalStateException( String.format("registration timeout after %s milli seconds.", configurationProperties.getRegistrationWaitTimeInMillis())); } } catch (InterruptedException e) { throw new IllegalStateException("registration process is interrupted."); } } @Subscribe public void onMicroserviceInstanceRegistrationEvent(MicroserviceInstanceRegistrationEvent event) { if (!event.isSuccess()) { return; } readyWaiter.countDown(); if (configurationProperties.isWatch()) { serviceCenterWatch.startWatch(SCConst.SC_DEFAULT_PROJECT, microservice.getServiceId()); } } @Override public void destroy() { if (serviceCenterRegistration != null) { serviceCenterRegistration.stop(); } } @Override public String name() { return SCConst.SC_REGISTRY_NAME; } @Override public SCRegistrationInstance getMicroserviceInstance() { return registrationInstance; } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { return serviceCenterClient.updateMicroserviceInstanceStatus(microservice.getServiceId(), microserviceInstance.getInstanceId(), org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus.valueOf(status.name())); } @Override public void addSchema(String schemaId, String content) { if (configurationProperties.isEnableSwaggerRegistration()) { this.microservice.addSchema(schemaId); this.serviceCenterRegistration.addSchemaInfo( new SchemaInfo(schemaId, content, calcSchemaSummary(content))); } } @SuppressWarnings("UnstableApiUsage") public static String calcSchemaSummary(String schemaContent) { return Hashing.sha256().newHasher().putString(schemaContent, Charsets.UTF_8).hash().toString(); } @Override public void addEndpoint(String endpoint) { this.microserviceInstance.addEndpoint(endpoint); } @Override public void addProperty(String key, String value) { this.microserviceInstance.addProperty(key, value); } @Override public boolean enabled() { return this.configurationProperties.isEnabled(); } public Microservice getBackendMicroservice() { return microservice; } public MicroserviceInstance getBackendMicroserviceInstance() { return microserviceInstance; } EventBus getEventBus() { return eventBus; } } ================================================ FILE: service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.sc; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.apache.servicecomb.service.center.client.ServiceCenterRegistration; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; public class SCRegistrationInstance implements RegistrationInstance { private final Microservice microservice; private final MicroserviceInstance microserviceInstance; private final ServiceCenterRegistration serviceCenterRegistration; public SCRegistrationInstance(Microservice microservice, MicroserviceInstance microserviceInstance, ServiceCenterRegistration serviceCenterRegistration) { this.microservice = microservice; this.microserviceInstance = microserviceInstance; this.serviceCenterRegistration = serviceCenterRegistration; } @Override public String getEnvironment() { return microservice.getEnvironment(); } @Override public String getApplication() { return microservice.getAppId(); } @Override public String getServiceName() { return microservice.getServiceName(); } @Override public String getAlias() { return microservice.getAlias(); } @Override public String getVersion() { return microservice.getVersion(); } @Override public DataCenterInfo getDataCenterInfo() { return new DataCenterInfo(microserviceInstance.getDataCenterInfo().getName(), microserviceInstance.getDataCenterInfo().getRegion(), microserviceInstance.getDataCenterInfo().getAvailableZone()); } @Override public String getDescription() { return microservice.getDescription(); } @Override public Map getProperties() { return microserviceInstance.getProperties(); } @Override public Map getSchemas() { Map result = new HashMap<>(); serviceCenterRegistration.getSchemaInfos().forEach(info -> result.put(info.getSchemaId(), info.getSchema())); return result; } @Override public List getEndpoints() { return microserviceInstance.getEndpoints(); } @Override public String getInstanceId() { return microserviceInstance.getInstanceId(); } @Override public String getServiceId() { return microservice.getServiceId(); } @Override public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.valueOf(microserviceInstance.getStatus().name()); } public Microservice getBackendMicroservice() { return microservice; } public MicroserviceInstance getBackendMicroserviceInstance() { return microserviceInstance; } } ================================================ FILE: service-registry/registry-service-center/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.sc.SCConfiguration ================================================ FILE: service-registry/registry-zero-config/pom.xml ================================================ service-registry-parent org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 registry-zero-config Java Chassis::Service Registry::Zero Config org.apache.servicecomb registry-lightweight ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.ORDER; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.apache.servicecomb.registry.lightweight.AbstractLightweightRegistration; import org.springframework.beans.factory.annotation.Autowired; public abstract class AbstractZeroConfigRegistration extends AbstractLightweightRegistration { protected Config config; @Autowired public AbstractZeroConfigRegistration setConfig(Config config) { this.config = config; return this; } @Override public int getOrder() { return ORDER; } @Override public boolean enabled() { return config.isEnabled(); } @Override public void run() { super.run(); startRegister(config.getHeartbeatInterval()); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/Config.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_ENABLED; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_HEARTBEAT_INTERVAL; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_HEARTBEAT_LOST_TIMES; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_MULTICAST_ADDRESS; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_MULTICAST_GROUP; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_PULL_INTERVAL; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_ADDRESS; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_GROUP; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_HEARTBEAT_INTERVAL; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_HEARTBEAT_LOST_TIMES; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_PULL_INTERVAL; import java.time.Duration; import org.apache.servicecomb.config.DynamicProperties; import org.springframework.beans.factory.annotation.Autowired; public class Config { private DynamicProperties dynamicProperties; @Autowired public Config setDynamicProperties(DynamicProperties dynamicProperties) { this.dynamicProperties = dynamicProperties; return this; } public boolean isEnabled() { return dynamicProperties.getBooleanProperty(CFG_ENABLED, true); } public String getMulticastAddress() { return dynamicProperties.getStringProperty(CFG_MULTICAST_ADDRESS, DEFAULT_ADDRESS); } // (224.0.0.0, 239.255.255.255] public String getMulticastGroup() { return dynamicProperties.getStringProperty(CFG_MULTICAST_GROUP, DEFAULT_GROUP); } public Duration getHeartbeatInterval() { String interval = dynamicProperties.getStringProperty(CFG_HEARTBEAT_INTERVAL, DEFAULT_HEARTBEAT_INTERVAL); return toDuration(interval); } public Duration getCheckDeadInstancesInterval() { int lostTimes = dynamicProperties.getIntProperty(CFG_HEARTBEAT_LOST_TIMES, DEFAULT_HEARTBEAT_LOST_TIMES); return getHeartbeatInterval().multipliedBy(lostTimes); } public Duration getPullInterval() { String interval = dynamicProperties.getStringProperty(CFG_PULL_INTERVAL, DEFAULT_PULL_INTERVAL); return toDuration(interval); } private Duration toDuration(String interval) { return Duration.parse("PT" + interval); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; public interface ZeroConfigConst { String ZERO_CONFIG_REGISTRY_NAME = "zero-config-registry"; int MAX_PACKET_SIZE = 1024; String PREFIX = "servicecomb.registry.zero-config."; String CFG_ENABLED = PREFIX + "enabled"; String CFG_MULTICAST_GROUP = PREFIX + "multicast.group"; String CFG_MULTICAST_ADDRESS = PREFIX + "multicast.address"; String CFG_HEARTBEAT_INTERVAL = PREFIX + "heartbeat.interval"; String CFG_HEARTBEAT_LOST_TIMES = PREFIX + "heartbeat.lost-times"; String CFG_PULL_INTERVAL = PREFIX + "pull-interval"; String DEFAULT_GROUP = "225.6.7.8"; String DEFAULT_ADDRESS = "0.0.0.0:6666"; String DEFAULT_HEARTBEAT_INTERVAL = "30s"; int DEFAULT_HEARTBEAT_LOST_TIMES = 3; String DEFAULT_PULL_INTERVAL = "3s"; int ORDER = -8000; } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.ORDER; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.apache.servicecomb.registry.lightweight.AbstractLightweightDiscovery; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; import org.springframework.beans.factory.annotation.Autowired; public class ZeroConfigDiscovery extends AbstractLightweightDiscovery { private Config config; @Autowired public ZeroConfigDiscovery setConfig(Config config) { this.config = config; return this; } @Override public String name() { return ZeroConfigConst.ZERO_CONFIG_REGISTRY_NAME; } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { } @Override public int getOrder() { return ORDER; } @Override public boolean enabled() { return config.isEnabled(); } @Override public void run() { } @Override public List findServiceInstances(String application, String serviceName) { MicroserviceInstances microserviceInstances = store.findServiceInstances(application, serviceName, "0"); if (microserviceInstances.isMicroserviceNotExist() || microserviceInstances.getInstancesResponse() == null) { return Collections.emptyList(); } List result = new ArrayList<>(); for (MicroserviceInstance instance : microserviceInstances.getInstancesResponse().getInstances()) { result.add(new ZeroConfigDiscoveryInstance(store.getMicroservice(instance.getServiceId()).get(), instance)); } return result; } @Override public List findServices(String application) { List microservices = store.getAllMicroservices(); return microservices.stream().filter(e -> e.getAppId().equals(application)) .map(Microservice::getServiceName).collect(Collectors.toList()); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; public class ZeroConfigDiscoveryInstance extends AbstractDiscoveryInstance { private final Microservice microservice; private final MicroserviceInstance microserviceInstance; public ZeroConfigDiscoveryInstance(Microservice microservice, MicroserviceInstance microserviceInstance) { this.microservice = microservice; this.microserviceInstance = microserviceInstance; } @Override public MicroserviceInstanceStatus getStatus() { return microserviceInstance.getStatus(); } @Override public String getRegistryName() { return ZeroConfigConst.ZERO_CONFIG_REGISTRY_NAME; } @Override public String getEnvironment() { return microservice.getEnvironment(); } @Override public String getApplication() { return microservice.getAppId(); } @Override public String getServiceName() { return microservice.getServiceName(); } @Override public String getAlias() { return microservice.getAlias(); } @Override public String getVersion() { return microservice.getVersion(); } @Override public DataCenterInfo getDataCenterInfo() { return microserviceInstance.getDataCenterInfo(); } @Override public String getDescription() { return microservice.getDescription(); } @Override public Map getProperties() { return microserviceInstance.getProperties(); } @Override public Map getSchemas() { return microservice.getSchemaMap(); } @Override public List getEndpoints() { return microserviceInstance.getEndpoints(); } @Override public String getInstanceId() { return microserviceInstance.getInstanceId(); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import java.io.IOException; import org.apache.servicecomb.registry.lightweight.MessageType; import org.apache.servicecomb.registry.lightweight.RegisterInstanceEvent; import org.apache.servicecomb.zeroconfig.multicast.Multicast; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.eventbus.Subscribe; @SuppressWarnings("UnstableApiUsage") public class ZeroConfigRegistration extends AbstractZeroConfigRegistration implements InitializingBean { protected Multicast multicast; @Autowired public ZeroConfigRegistration setMulticast(Multicast multicast) { this.multicast = multicast; return this; } @Override public String name() { return ZeroConfigConst.ZERO_CONFIG_REGISTRY_NAME; } // delete after support @Conditional @Override public boolean enabled() { return true; } @Override public void afterPropertiesSet() { eventBus.register(this); } @Override protected void doSendRegister() throws IOException { multicast.send(MessageType.REGISTER, self.buildRegisterRequest()); } @Override protected void doSendUnregister() throws IOException { multicast.send(MessageType.UNREGISTER, self.buildUnregisterRequest()); } @Override public ZeroConfigRegistrationInstance getMicroserviceInstance() { return new ZeroConfigRegistrationInstance(self); } @SuppressWarnings("unused") @Subscribe public void onRegisterInstance(RegisterInstanceEvent event) { if (event.getInstance().getInstanceId().equals(self.getInstance().getInstanceId())) { return; } sendRegister(); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.RegistrationInstance; import org.apache.servicecomb.registry.lightweight.Self; public class ZeroConfigRegistrationInstance implements RegistrationInstance { private final Self self; public ZeroConfigRegistrationInstance(Self self) { this.self = self; } @Override public String getEnvironment() { return self.getMicroservice().getEnvironment(); } @Override public String getApplication() { return self.getMicroservice().getAppId(); } @Override public String getServiceName() { return self.getMicroservice().getServiceName(); } @Override public String getAlias() { return self.getMicroservice().getAlias(); } @Override public String getVersion() { return self.getMicroservice().getVersion(); } @Override public DataCenterInfo getDataCenterInfo() { return self.getInstance().getDataCenterInfo(); } @Override public String getDescription() { return self.getMicroservice().getDescription(); } @Override public Map getProperties() { return self.getInstance().getProperties(); } @Override public Map getSchemas() { return this.self.getMicroservice().getSchemaMap(); } @Override public List getEndpoints() { return this.self.getInstance().getEndpoints(); } @Override public String getInstanceId() { return this.self.getInstanceId(); } @Override public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.UP; } public void addSchema(String schemaId, String content) { this.self.getMicroservice().addSchema(schemaId, content); } public void addEndpoint(String endpoint) { this.self.getInstance().getEndpoints().add(endpoint); } public void addProperty(String key, String value) { this.self.getInstance().getProperties().put(key, value); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistryConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig; import java.io.IOException; import org.apache.servicecomb.registry.lightweight.MessageExecutor; import org.apache.servicecomb.zeroconfig.multicast.Multicast; import org.apache.servicecomb.zeroconfig.multicast.MulticastServer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ZeroConfigRegistryConfiguration { @Bean public ZeroConfigRegistration zeroConfigRegistration() { return new ZeroConfigRegistration(); } @Bean public MulticastServer multicastServer(Config config, Multicast multicast, MessageExecutor messageExecutor) { return new MulticastServer(config, multicast, messageExecutor); } @Bean public ZeroConfigDiscovery zeroConfigDiscovery() { return new ZeroConfigDiscovery(); } @Bean public Multicast zeroConfigMulticast(Config config) throws IOException { return new Multicast(config); } @Bean public Config zeroConfigModel() { return new Config(); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/Multicast.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig.multicast; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.registry.lightweight.Message; import org.apache.servicecomb.registry.lightweight.MessageType; import org.apache.servicecomb.zeroconfig.Config; import org.apache.servicecomb.zeroconfig.ZeroConfigConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.net.HostAndPort; public class Multicast { private static final Logger LOGGER = LoggerFactory.getLogger(Multicast.class); private final InetSocketAddress bindAddress; // (224.0.0.0, 239.255.255.255] private final InetAddress group; private final MulticastSocket multicastSocket; private final byte[] recvBuffer = new byte[ZeroConfigConst.MAX_PACKET_SIZE]; private final DatagramPacket recvPacket = new DatagramPacket(recvBuffer, recvBuffer.length); @SuppressWarnings("deprecation") public Multicast(Config config) throws IOException { this.bindAddress = initBindAddress(config); this.group = initGroup(config); LOGGER.info("zero config, address: {}", bindAddress); LOGGER.info("zero config, group: {}", group); this.multicastSocket = new MulticastSocket(bindAddress); this.multicastSocket.joinGroup(group); this.multicastSocket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(5)); } public Multicast setSendBufferSize(int size) throws SocketException { multicastSocket.setSendBufferSize(size); return this; } public Multicast setReceiveBufferSize(int size) throws SocketException { multicastSocket.setReceiveBufferSize(size); return this; } @SuppressWarnings("UnstableApiUsage") private InetSocketAddress initBindAddress(Config config) { HostAndPort hostAndPort = HostAndPort.fromString(config.getMulticastAddress()); return new InetSocketAddress(hostAndPort.getHost(), hostAndPort.getPort()); } private InetAddress initGroup(Config config) throws UnknownHostException { return InetAddress.getByName(config.getMulticastGroup()); } public void send(MessageType type, T body) throws IOException { byte[] buffer = Message.of(type, body).encode(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, bindAddress.getPort()); multicastSocket.send(packet); } public Message recv() throws IOException { multicastSocket.receive(recvPacket); return Message.decode(recvPacket.getData(), recvPacket.getLength()); } } ================================================ FILE: service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/MulticastServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.zeroconfig.multicast; import java.net.SocketTimeoutException; import java.util.concurrent.Executors; import org.apache.servicecomb.registry.lightweight.Message; import org.apache.servicecomb.registry.lightweight.MessageExecutor; import org.apache.servicecomb.zeroconfig.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings("unused") public class MulticastServer { private static final Logger LOGGER = LoggerFactory.getLogger(MulticastServer.class); private final Multicast multicast; private final MessageExecutor messageExecutor; public MulticastServer(Config config, Multicast multicast, MessageExecutor messageExecutor) { this.multicast = multicast; this.messageExecutor = messageExecutor; Executors .newSingleThreadExecutor(runnable -> new Thread(runnable, "multicast-server-recv")) .execute(this::recv); messageExecutor.startCheckDeadInstances(config.getCheckDeadInstancesInterval()); } @SuppressWarnings("InfiniteLoopStatement") private void recv() { for (; ; ) { Message message = recvMsg(); if (message == null) { continue; } messageExecutor.processMessage(message); } } private Message recvMsg() { try { return multicast.recv(); } catch (SocketTimeoutException ignore) { return null; } catch (Exception e) { LOGGER.error("failed to receive or decode message.", e); return null; } } } ================================================ FILE: service-registry/registry-zero-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.zeroconfig.ZeroConfigRegistryConfiguration ================================================ FILE: service-registry/registry-zookeeper/pom.xml ================================================ org.apache.servicecomb service-registry-parent 3.4.0-SNAPSHOT 4.0.0 registry-zookeeper Java Chassis::Service Registry::Zookeeper org.apache.curator curator-x-discovery org.apache.curator curator-framework org.apache.servicecomb foundation-common org.apache.servicecomb foundation-registry org.apache.servicecomb java-chassis-core ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ZookeeperConfiguration { @Bean @ConfigurationProperties(prefix = ZookeeperConst.ZOOKEEPER_REGISTRY_PREFIX) public ZookeeperRegistryProperties zookeeperRegistryProperties() { return new ZookeeperRegistryProperties(); } @Bean public ZookeeperDiscovery zookeeperDiscovery() { return new ZookeeperDiscovery(); } @Bean public ZookeeperRegistration zookeeperRegistration() { return new ZookeeperRegistration(); } } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; public class ZookeeperConst { public static final String ZOOKEEPER_REGISTRY_NAME = "zookeeper-registry"; public static final String ZOOKEEPER_DISCOVERY_ROOT = "/servicecomb/registry/%s"; public static final String ZOOKEEPER_REGISTRY_PREFIX = "servicecomb.registry.zk"; public static final String ZOOKEEPER_DISCOVERY_ENABLED = ZOOKEEPER_REGISTRY_PREFIX + ".%s.%s.enabled"; public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import org.apache.curator.x.discovery.details.ServiceCacheListener; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.registry.api.Discovery; import org.apache.zookeeper.server.auth.DigestLoginModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class ZookeeperDiscovery implements Discovery { static class ZookeeperSASLConfig extends Configuration { AppConfigurationEntry entry; public ZookeeperSASLConfig(String username, String password) { Map options = new HashMap<>(); options.put("username", username); options.put("password", password); this.entry = new AppConfigurationEntry( DigestLoginModule.class.getName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options ); } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { AppConfigurationEntry[] array = new AppConfigurationEntry[1]; array[0] = entry; return array; } } private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperDiscovery.class); private final Map>> serviceDiscoveries = new ConcurrentHashMapEx<>(); private final Map> serviceNameDiscoveries = new ConcurrentHashMapEx<>(); private Environment environment; private ZookeeperRegistryProperties zookeeperRegistryProperties; private String basePath; private CuratorFramework client; private InstanceChangedListener instanceChangedListener; @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired @SuppressWarnings("unused") public void setZookeeperRegistryProperties(ZookeeperRegistryProperties zookeeperRegistryProperties) { this.zookeeperRegistryProperties = zookeeperRegistryProperties; } @Override public String name() { return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; } @Override public boolean enabled(String application, String serviceName) { return environment.getProperty(String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ENABLED, application, serviceName), boolean.class, true); } @Override public List findServiceInstances(String application, String serviceName) { try { ServiceCache discovery = serviceDiscoveries.computeIfAbsent(application, app -> new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, name -> { JsonInstanceSerializer serializer = new JsonInstanceSerializer<>(ZookeeperInstance.class); ServiceDiscovery dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) .client(client) .basePath(basePath + "/" + application) .serializer(serializer) .build(); ServiceCache cache = dis.serviceCacheBuilder().name(serviceName).build(); cache.addListener(new ServiceCacheListener() { @Override public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) { LOGGER.warn("zookeeper discovery state changed {}", connectionState); } @Override public void cacheChanged() { instanceChangedListener.onInstanceChanged(name(), application, serviceName, toDiscoveryInstances(cache.getInstances())); } }); try { CountDownLatch latch = cache.startImmediate(); if (!latch.await(5000, TimeUnit.SECONDS)) { throw new IllegalStateException("cache start failed."); } } catch (Exception e) { throw new IllegalStateException(e); } return cache; }); List> instances = discovery.getInstances(); return toDiscoveryInstances(instances); } catch (Exception e) { throw new IllegalStateException(e); } } @Override public List findServices(String application) { try { ServiceDiscovery discovery = serviceNameDiscoveries .computeIfAbsent(application, app -> { JsonInstanceSerializer serializer = new JsonInstanceSerializer<>(ZookeeperInstance.class); ServiceDiscovery dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) .client(client) .basePath(basePath + "/" + application) .serializer(serializer) .build(); try { dis.start(); } catch (Exception e) { throw new IllegalStateException(e); } return dis; }); return discovery.queryForNames().stream().toList(); } catch (Exception e) { throw new IllegalStateException(e); } } private List toDiscoveryInstances( List> instances) { return instances.stream().map(instance -> new ZookeeperDiscoveryInstance(instance.getPayload())).collect(Collectors.toList()); } @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { this.instanceChangedListener = instanceChangedListener; } @Override public void init() { String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = ZookeeperConst.ZOOKEEPER_DEFAULT_ENVIRONMENT; } basePath = String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ROOT, env); } @Override public void run() { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(zookeeperRegistryProperties.getConnectString()) .sessionTimeoutMs(zookeeperRegistryProperties.getSessionTimeoutMillis()) .retryPolicy(new ExponentialBackoffRetry(1000, 3)); String authSchema = zookeeperRegistryProperties.getAuthenticationSchema(); if (StringUtils.isNotEmpty(authSchema)) { if (!"digest".equals(authSchema)) { throw new IllegalStateException("Not supported schema now. " + authSchema); } if (zookeeperRegistryProperties.getAuthenticationInfo() == null) { throw new IllegalStateException("Auth info can not be empty. "); } String[] authInfo = zookeeperRegistryProperties.getAuthenticationInfo().split(":"); Configuration.setConfiguration(new ZookeeperSASLConfig(authInfo[0], authInfo[1])); } client = builder.build(); client.start(); } @Override public void destroy() { if (client != null) { CloseableUtils.closeQuietly(client); } } @Override public boolean enabled() { return zookeeperRegistryProperties.isEnabled(); } } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; import org.apache.servicecomb.registry.api.DiscoveryInstance; public class ZookeeperDiscoveryInstance extends ZookeeperInstance implements DiscoveryInstance { public ZookeeperDiscoveryInstance(ZookeeperInstance other) { super(other); } @Override public String getRegistryName() { return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; } } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstance; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; public class ZookeeperInstance implements MicroserviceInstance { private String serviceId; private String instanceId; private String environment; private String application; private String serviceName; private String alias; private String version; private String description; private DataCenterInfo dataCenterInfo; private List endpoints = new ArrayList<>(); private Map schemas = new HashMap<>(); private Map properties = new HashMap<>(); private MicroserviceInstanceStatus status; public ZookeeperInstance() { } public ZookeeperInstance(ZookeeperInstance other) { this.serviceId = other.serviceId; this.instanceId = other.instanceId; this.environment = other.environment; this.application = other.application; this.serviceName = other.serviceName; this.alias = other.alias; this.version = other.version; this.description = other.description; this.dataCenterInfo = other.dataCenterInfo; this.endpoints = other.endpoints; this.schemas = other.schemas; this.properties = other.properties; this.status = other.status; } public void setServiceId(String serviceId) { this.serviceId = serviceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public void setEnvironment(String environment) { this.environment = environment; } public void setApplication(String application) { this.application = application; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public void setAlias(String alias) { this.alias = alias; } public void setVersion(String version) { this.version = version; } public void setDescription(String description) { this.description = description; } public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { this.dataCenterInfo = dataCenterInfo; } public void setEndpoints(List endpoints) { this.endpoints = endpoints; } public void setSchemas(Map schemas) { this.schemas = schemas; } public void setProperties(Map properties) { this.properties = properties; } public void setStatus(MicroserviceInstanceStatus status) { this.status = status; } @Override public String getEnvironment() { return this.environment; } @Override public String getApplication() { return this.application; } @Override public String getServiceName() { return this.serviceName; } @Override public String getAlias() { return alias; } @Override public String getVersion() { return version; } @Override public DataCenterInfo getDataCenterInfo() { return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; } @Override public String getDescription() { return description; } @Override public Map getProperties() { return properties; } @Override public Map getSchemas() { return schemas; } @Override public List getEndpoints() { return endpoints; } public void addSchema(String schemaId, String content) { this.schemas.put(schemaId, content); } public void addEndpoint(String endpoint) { this.endpoints.add(endpoint); } public void addProperty(String key, String value) { this.properties.put(key, value); } @Override public String getInstanceId() { return instanceId; } @Override public String getServiceId() { return serviceId; } @Override public MicroserviceInstanceStatus getStatus() { return this.status; } } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; import java.util.HashMap; import java.util.Map; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.apache.zookeeper.server.auth.DigestLoginModule; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; public class ZookeeperRegistration implements Registration { static class ZookeeperSASLConfig extends Configuration { AppConfigurationEntry entry; public ZookeeperSASLConfig(String username, String password) { Map options = new HashMap<>(); options.put("username", username); options.put("password", password); this.entry = new AppConfigurationEntry( DigestLoginModule.class.getName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options ); } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { AppConfigurationEntry[] array = new AppConfigurationEntry[1]; array[0] = entry; return array; } } private Environment environment; private ZookeeperRegistryProperties zookeeperRegistryProperties; private DataCenterProperties dataCenterProperties; private String basePath; private CuratorFramework client; private ServiceInstance instance; private RegistrationId registrationId; private ServiceDiscovery dis; @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired @SuppressWarnings("unused") public void setZookeeperRegistryProperties(ZookeeperRegistryProperties zookeeperRegistryProperties) { this.zookeeperRegistryProperties = zookeeperRegistryProperties; } @Autowired public void setRegistrationId(RegistrationId registrationId) { this.registrationId = registrationId; } @Autowired @SuppressWarnings("unused") public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { this.dataCenterProperties = dataCenterProperties; } @Override public void init() { String env = BootStrapProperties.readServiceEnvironment(environment); if (StringUtils.isEmpty(env)) { env = ZookeeperConst.ZOOKEEPER_DEFAULT_ENVIRONMENT; } basePath = String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ROOT, env); ZookeeperInstance zookeeperInstance = new ZookeeperInstance(); zookeeperInstance.setInstanceId(registrationId.getInstanceId()); zookeeperInstance.setEnvironment(env); zookeeperInstance.setApplication(BootStrapProperties.readApplication(environment)); zookeeperInstance.setServiceName(BootStrapProperties.readServiceName(environment)); zookeeperInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); zookeeperInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { DataCenterInfo dataCenterInfo = new DataCenterInfo(); dataCenterInfo.setName(dataCenterProperties.getName()); dataCenterInfo.setRegion(dataCenterProperties.getRegion()); dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); zookeeperInstance.setDataCenterInfo(dataCenterInfo); } zookeeperInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); zookeeperInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); zookeeperInstance.setStatus( MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); try { this.instance = ServiceInstance.builder().name(zookeeperInstance.getServiceName()) .id(zookeeperInstance.getInstanceId()).payload(zookeeperInstance).build(); } catch (Exception e) { throw new IllegalStateException(e); } } @Override public void run() { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(zookeeperRegistryProperties.getConnectString()) .sessionTimeoutMs(zookeeperRegistryProperties.getSessionTimeoutMillis()) .retryPolicy(new ExponentialBackoffRetry(1000, 3)); String authSchema = zookeeperRegistryProperties.getAuthenticationSchema(); if (StringUtils.isNotEmpty(authSchema)) { if (!"digest".equals(authSchema)) { throw new IllegalStateException("Not supported schema now. " + authSchema); } if (zookeeperRegistryProperties.getAuthenticationInfo() == null) { throw new IllegalStateException("Auth info can not be empty. "); } String[] authInfo = zookeeperRegistryProperties.getAuthenticationInfo().split(":"); Configuration.setConfiguration(new ZookeeperDiscovery.ZookeeperSASLConfig(authInfo[0], authInfo[1])); } client = builder.build(); client.start(); JsonInstanceSerializer serializer = new JsonInstanceSerializer<>(ZookeeperInstance.class); dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) .client(client) .basePath(basePath + "/" + BootStrapProperties.readApplication(environment)) .serializer(serializer) .thisInstance(instance) .build(); try { dis.start(); } catch (Exception e) { throw new IllegalStateException(e); } } @Override public void destroy() { if (client != null) { CloseableUtils.closeQuietly(client); } } @Override public String name() { return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; } @Override public ZookeeperRegistrationInstance getMicroserviceInstance() { return new ZookeeperRegistrationInstance(instance.getPayload()); } @Override public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { this.instance.getPayload().setStatus(status); try { dis.updateService(instance); } catch (Exception e) { throw new IllegalStateException(e); } return true; } @Override public void addSchema(String schemaId, String content) { if (zookeeperRegistryProperties.isEnableSwaggerRegistration()) { instance.getPayload().addSchema(schemaId, content); } } @Override public void addEndpoint(String endpoint) { instance.getPayload().addEndpoint(endpoint); } @Override public void addProperty(String key, String value) { instance.getPayload().addProperty(key, value); } @Override public boolean enabled() { return zookeeperRegistryProperties.isEnabled(); } } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; import org.apache.servicecomb.registry.api.RegistrationInstance; public class ZookeeperRegistrationInstance extends ZookeeperInstance implements RegistrationInstance { public ZookeeperRegistrationInstance(ZookeeperInstance instance) { super(instance); } } ================================================ FILE: service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.registry.zookeeper; public class ZookeeperRegistryProperties { private boolean enabled = true; private boolean ephemeral = true; private String connectString = "127.0.0.1:2181"; private String authenticationSchema; private String authenticationInfo; private int connectionTimeoutMillis = 1000; private int sessionTimeoutMillis = 60000; private boolean enableSwaggerRegistration = false; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isEphemeral() { return ephemeral; } public void setEphemeral(boolean ephemeral) { this.ephemeral = ephemeral; } public String getConnectString() { return connectString; } public void setConnectString(String connectString) { this.connectString = connectString; } public int getConnectionTimeoutMillis() { return connectionTimeoutMillis; } public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { this.connectionTimeoutMillis = connectionTimeoutMillis; } public int getSessionTimeoutMillis() { return sessionTimeoutMillis; } public void setSessionTimeoutMillis(int sessionTimeoutMillis) { this.sessionTimeoutMillis = sessionTimeoutMillis; } public boolean isEnableSwaggerRegistration() { return enableSwaggerRegistration; } public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { this.enableSwaggerRegistration = enableSwaggerRegistration; } public String getAuthenticationSchema() { return authenticationSchema; } public void setAuthenticationSchema(String authenticationSchema) { this.authenticationSchema = authenticationSchema; } public String getAuthenticationInfo() { return authenticationInfo; } public void setAuthenticationInfo(String authenticationInfo) { this.authenticationInfo = authenticationInfo; } } ================================================ FILE: service-registry/registry-zookeeper/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.registry.zookeeper.ZookeeperConfiguration ================================================ FILE: solutions/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 solutions Java Chassis::Solutions pom solution-basic ================================================ FILE: solutions/solution-basic/pom.xml ================================================ org.apache.servicecomb solutions 3.4.0-SNAPSHOT 4.0.0 solution-basic Java Chassis::Solutions::BASIC org.apache.servicecomb transport-rest-vertx org.apache.servicecomb transport-highway org.apache.servicecomb transport-rest-servlet org.apache.servicecomb provider-springmvc org.apache.servicecomb provider-pojo org.apache.servicecomb provider-jaxrs org.apache.servicecomb handler-fault-injection org.apache.servicecomb handler-flowcontrol-qps org.apache.servicecomb handler-governance org.apache.servicecomb handler-loadbalance org.apache.servicecomb handler-publickey-auth org.apache.servicecomb handler-router org.apache.servicecomb swagger-invocation-validator org.apache.servicecomb metrics-core ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.Map; import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; /** * Java Chassis health check extensions. */ @Path("/scb/health") public interface HealthEndpoint { String NAME = "scb-health"; @Path("/") @GET boolean checkHealth(); @Path("/details") @GET Map checkHealthDetails(); } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpointImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.Map; import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; import org.apache.servicecomb.foundation.metrics.health.HealthCheckerManager; import org.apache.servicecomb.provider.rest.common.RestSchema; @RestSchema(schemaId = HealthEndpoint.NAME, schemaInterface = HealthEndpoint.class) public class HealthEndpointImpl implements HealthEndpoint { @Override public boolean checkHealth() { Map results = HealthCheckerManager.getInstance().check(); for (HealthCheckResult result : results.values()) { if (!result.isHealthy()) { return false; } } return true; } @Override public Map checkHealthDetails() { return HealthCheckerManager.getInstance().check(); } } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.registry.discovery.InstancePing; import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; import org.apache.servicecomb.registry.discovery.TelnetInstancePing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; public class HealthInstancePing implements InstancePing { private static final Logger LOGGER = LoggerFactory.getLogger(HealthInstancePing.class); private SCBEngine scbEngine; private TelnetInstancePing telnetInstancePing; private Environment environment; @Autowired @Lazy public void setScbEngine(SCBEngine scbEngine) { this.scbEngine = scbEngine; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired @Lazy public void setTelnetInstancePing(@Qualifier("scbTelnetInstancePing") TelnetInstancePing telnetInstancePing) { this.telnetInstancePing = telnetInstancePing; } @Override public boolean ping(StatefulDiscoveryInstance instance) { if (CollectionUtils.isEmpty(instance.getEndpoints())) { return false; } // hard coded here, not very nice if ("local-registry".equals(instance.getRegistryName())) { return telnetInstancePing.ping(instance); } Map args = new HashMap<>(2); args.put("instanceId", instance.getInstanceId()); args.put("registryName", instance.getRegistryName()); for (String endpoint : instance.getEndpoints()) { URI uri = URI.create(endpoint); String transportName = uri.getScheme(); Transport transport = scbEngine.getTransportManager().findTransport(transportName); if (transport == null) { continue; } // Use myself service name instead of the target. Because can avoid create // MicroserviceReferenceConfig for the target. Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), transportName, ManagementEndpoint.NAME, "health", args, boolean.class); invocation.setEndpoint(new Endpoint(transport, endpoint, instance)); boolean result; try { result = (boolean) InvokerUtils.syncInvoke(invocation); } catch (Exception e) { LOGGER.warn("ping instance {}/{}/{}/{} endpoint {} failed. {}", instance.getApplication(), instance.getServiceName(), instance.getRegistryName(), instance.getInstanceId(), endpoint, e.getMessage()); continue; } if (result) { return true; } LOGGER.warn("ping instance {}/{}/{}/{} endpoint {} failed", instance.getApplication(), instance.getServiceName(), instance.getRegistryName(), instance.getInstanceId(), endpoint); } return false; } @Override public int getOrder() { return -10000; } } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.provider.OpenAPIRegistry; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.apache.servicecomb.core.provider.consumer.InvokerUtils; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.registry.DiscoveryManager; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.router.util.VersionCompareUtil; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.core.type.TypeReference; import io.swagger.v3.oas.models.OpenAPI; import jakarta.ws.rs.core.Response.Status; public class InstanceOpenAPIRegistry implements OpenAPIRegistry { private static final Logger LOGGER = LoggerFactory.getLogger(InstanceOpenAPIRegistry.class); private DiscoveryManager discoveryManager; private Environment environment; private TransportManager transportManager; @Autowired public void setDiscoveryManager(DiscoveryManager discoveryManager) { this.discoveryManager = discoveryManager; } @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Autowired public void setTransportManager(TransportManager transportManager) { this.transportManager = transportManager; } @Override public boolean enabled() { return environment.getProperty(OpenAPIRegistry.CONFIG_PREFIX + ".instance.enabled", boolean.class, true); } @Override public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { // do nothing } @Override @SuppressWarnings("unchecked") public Map loadOpenAPI(String application, String serviceName) { List discoveryInstances = discoveryManager.getOrCreateVersionedCache(application, serviceName).data(); if (discoveryInstances.isEmpty()) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); } discoveryInstances.sort((a, b) -> VersionCompareUtil.compareVersion(b.getVersion(), a.getVersion())); String version = null; for (DiscoveryInstance instance : discoveryInstances) { if (version != null && !version.equals(instance.getVersion())) { break; } version = instance.getVersion(); for (String endpoint : instance.getEndpoints()) { URI uri = URI.create(endpoint); String transportName = uri.getScheme(); Transport transport = transportManager.findTransport(transportName); if (transport == null) { continue; } // Use myself service name instead of the target. Because can avoid create // MicroserviceReferenceConfig for the target. Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), transportName, ManagementEndpoint.NAME, "schemaContents", new HashMap<>(), new TypeReference>() { }.getType()); invocation.setEndpoint(new Endpoint(transport, endpoint, discoveryInstances.get(0))); try { Map contents = (Map) InvokerUtils.syncInvoke(invocation); Map result = new HashMap<>(contents.size()); contents.forEach((k, v) -> result.put(k, SwaggerUtils.parseSwagger(v))); return result; } catch (InvocationException e) { LOGGER.warn("Get schema contents {}/{}/{} from endpoint {} failed. {}", instance.getApplication(), instance.getServiceName(), instance.getInstanceId(), endpoint, e.getMessage()); } } } throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "Get schema contents fail from all latest version."); } @Override public void setOpenAPIChangeListener(OpenAPIChangeListener listener) { this.discoveryManager.addInstanceChangeListener( (registryName, application, serviceName, instances) -> { if (CollectionUtils.isEmpty(instances)) { return; } listener.onOpenAPIChanged(application, serviceName); }); } @Override public int getOrder() { return -9000; } } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/IntegrationConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class IntegrationConfiguration { @Bean public ManagementEndpointImpl managementEndpoint() { return new ManagementEndpointImpl(); } @Bean public HealthEndpointImpl healthEndpoint() { return new HealthEndpointImpl(); } @Bean public MetricsEndpointImpl metricsEndpoint() { return new MetricsEndpointImpl(); } @Bean public HealthInstancePing healthInstancePing() { return new HealthInstancePing(); } @Bean public InstanceOpenAPIRegistry instanceOpenAPIRegistry() { return new InstanceOpenAPIRegistry(); } } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.Map; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; /** * Java Chassis internal management apis. */ @Path("/scb/management") public interface ManagementEndpoint { String NAME = "scb-management"; /** * Health of this instance. If the instanceId match this instance, and this service is ready * to service return true. Otherwise, return false. * * This api is for internal instance status usage. Load balancer will call this api to check if * the target instance is alive. */ @GET @Path("/health") boolean health(@QueryParam("instanceId") String instanceId, @QueryParam("registryName") String registryName); /** * Schemas of this instance. * * This api is for internal schema loading usage. */ @POST @Path("/schema/contents") Map schemaContents(); } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.swagger.SwaggerUtils; import org.springframework.beans.factory.annotation.Autowired; import io.swagger.v3.oas.models.OpenAPI; @RestSchema(schemaId = ManagementEndpoint.NAME, schemaInterface = ManagementEndpoint.class) public class ManagementEndpointImpl implements ManagementEndpoint { private RegistrationManager registrationManager; private LocalOpenAPIRegistry localOpenAPIRegistry; @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; } @Autowired public void setLocalOpenAPIRegistry(LocalOpenAPIRegistry localOpenAPIRegistry) { this.localOpenAPIRegistry = localOpenAPIRegistry; } @Override public boolean health(String instanceId, String registryName) { if (StringUtils.isEmpty(instanceId) || StringUtils.isEmpty(registryName)) { return false; } String mySelf = registrationManager.getInstanceId(registryName); if (StringUtils.isEmpty(mySelf)) { return false; } return mySelf.equals(instanceId); } @Override public Map schemaContents() { Map apis = localOpenAPIRegistry.loadOpenAPI(); Map result = new HashMap<>(apis.size()); apis.forEach((k, v) -> result.put(k, SwaggerUtils.swaggerToString(v))); return result; } } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.Map; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; /** * Java Chassis metrics. */ @Path("/scb/metrics") public interface MetricsEndpoint { String NAME = "scb-metrics"; @GET @Path("/") Map measure(); } ================================================ FILE: solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.provider.rest.common.RestSchema; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; @RestSchema(schemaId = MetricsEndpoint.NAME, schemaInterface = MetricsEndpoint.class) public class MetricsEndpointImpl implements MetricsInitializer, MetricsEndpoint { private MeterRegistry meterRegistry; @Override public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { this.meterRegistry = meterRegistry; } @Override public Map measure() { Map measurements = new LinkedHashMap<>(); StringBuilder sb = new StringBuilder(); for (Meter meter : this.meterRegistry.getMeters()) { meter.measure().forEach(measurement -> { String key = idToString(meter.getId(), measurement, sb); measurements.put(key, measurement.getValue()); }); } return measurements; } // format id to string: // idName(tag1=value1,tag2=value2) protected String idToString(Id id, Measurement measurement, StringBuilder sb) { sb.setLength(0); sb.append(id.getName()).append("(").append(EndpointMeter.STATISTIC).append("=") .append(measurement.getStatistic().name()).append(","); sb.append(StreamSupport .stream(id .getTags() .spliterator(), false) .map(this::tagToString) .collect( Collectors.joining(","))); sb.append(')'); return sb.toString(); } private String tagToString(Tag tag) { return tag.getKey() + "=" + tag.getValue(); } } ================================================ FILE: solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.solution.basic.integration.IntegrationConfiguration ================================================ FILE: solutions/solution-basic/src/main/resources/microservice.yaml ================================================ # ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- # order of this configure file servicecomb-config-order: -100 servicecomb: # metrics and access log accesslog: enabled: true pattern: "|%SCB-traceId|%h|%r|%s|%D" metrics: window_time: 300000 invocation: latencyDistribution: 0,10,50,100,1000 Consumer.invocation.slow: enabled: true msTime: 1000 Provider.invocation.slow: enabled: true msTime: 1000 publisher.defaultLog: enabled: true endpoints.client.detail.enabled: true ================================================ FILE: solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import java.util.Map; import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; import org.apache.servicecomb.foundation.metrics.health.HealthChecker; import org.apache.servicecomb.foundation.metrics.health.HealthCheckerManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestHealthEndpointImpl { private final HealthChecker good = new HealthChecker() { @Override public String getName() { return "test"; } @Override public HealthCheckResult check() { return new HealthCheckResult(true, "info", "extra data"); } }; private final HealthChecker bad = new HealthChecker() { @Override public String getName() { return "test2"; } @Override public HealthCheckResult check() { return new HealthCheckResult(false, "info2", "extra data 2"); } }; @BeforeEach public void reset() { HealthCheckerManager.getInstance().unregister(good.getName()); HealthCheckerManager.getInstance().unregister(bad.getName()); } @Test public void checkHealthGood() { HealthCheckerManager.getInstance().register(good); HealthEndpointImpl publisher = new HealthEndpointImpl(); Assertions.assertTrue(publisher.checkHealth()); } @Test public void checkHealthBad() { HealthCheckerManager.getInstance().register(good); HealthCheckerManager.getInstance().register(bad); HealthEndpointImpl publisher = new HealthEndpointImpl(); Assertions.assertFalse(publisher.checkHealth()); } @Test public void checkHealthDetails() { HealthCheckerManager.getInstance().register(good); HealthCheckerManager.getInstance().register(bad); HealthEndpointImpl publisher = new HealthEndpointImpl(); Map content = publisher.checkHealthDetails(); Assertions.assertTrue(content.get("test").isHealthy()); Assertions.assertEquals("info", content.get("test").getInformation()); Assertions.assertEquals("extra data", content.get("test").getExtraData()); } } ================================================ FILE: solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; public class TestManagementEndpointImpl { } ================================================ FILE: solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.solution.basic.integration; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import java.util.Map; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class TestMetricsEndpointImpl { MetricsEndpointImpl publisher = new MetricsEndpointImpl(); EventBus eventBus = new EventBus(); Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); } @Test public void measure_globalRegistryNull() { MeterRegistry registry = new SimpleMeterRegistry(); publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment)); Map result = publisher.measure(); Assertions.assertEquals(0, result.size()); } @Test public void measure_normal() { MeterRegistry registry = new SimpleMeterRegistry(); registry.timer("name", "t1", "v1", "t2", "v2"); publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment)); Map result = publisher.measure(); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(0, result.get("name(statistic=COUNT,t1=v1,t2=v2)"), 0); Assertions.assertEquals(0, result.get("name(statistic=TOTAL_TIME,t1=v1,t2=v2)"), 0); Assertions.assertEquals(0, result.get("name(statistic=MAX,t1=v1,t2=v2)"), 0); } } ================================================ FILE: spring-boot/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 pom java-chassis-spring-boot Java Chassis::Spring Boot spring-boot-starters ================================================ FILE: spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml ================================================ java-chassis-spring-boot-starters org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 java-chassis-spring-boot-starter-servlet Java Chassis::Spring Boot Starter::Servlet org.springframework.boot spring-boot-starter-web org.apache.servicecomb transport-rest-servlet org.apache.servicecomb provider-springmvc org.apache.servicecomb provider-jaxrs org.apache.servicecomb provider-pojo org.apache.servicecomb handler-loadbalance jakarta.servlet jakarta.servlet-api compile org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/RestServletInitializer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.starter.servlet; import java.io.IOException; import java.net.ServerSocket; import org.apache.servicecomb.transport.rest.servlet.ServletUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.core.env.Environment; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; public class RestServletInitializer implements WebServerFactoryCustomizer, ServletContextInitializer { private static final Logger LOGGER = LoggerFactory.getLogger(RestServletInitializer.class); private AbstractConfigurableWebServerFactory factory = null; private Environment environment; @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void customize(AbstractConfigurableWebServerFactory factory) { this.factory = factory; } @Override @SuppressWarnings("try") public void onStartup(ServletContext servletContext) throws ServletException { if (this.factory == null) { // when running in external tomcat, WebServerFactoryCustomizer will not be available, but now tomcat // is already listening and we can call ServletUtils.init directly. ServletUtils.init(servletContext, environment); return; } if (factory.getPort() == 0) { LOGGER.warn( "spring boot embedded web container listen port is 0, ServiceComb will not use container's port to handler REST request."); return; } // when running in embedded tomcat, web container did not listen now. Call ServletUtils.init needs server is ready, // so mock to listen, and then close. try (ServerSocket ignored = new ServerSocket(factory.getPort(), 0, factory.getAddress())) { ServletUtils.init(servletContext, environment); } catch (IOException e) { throw new ServletException(e); } } } ================================================ FILE: spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/SpringBootStarterServletConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.starter.servlet; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SpringBootStarterServletConfiguration { @Bean public RestServletInitializer restServletInitializer() { return new RestServletInitializer(); } } ================================================ FILE: spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.springboot.starter.servlet; ================================================ FILE: spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.springboot.starter.servlet.SpringBootStarterServletConfiguration ================================================ FILE: spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml ================================================ java-chassis-spring-boot-starters org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 java-chassis-spring-boot-starter-standalone Java Chassis::Spring Boot Starter::Standalone org.apache.servicecomb transport-rest-vertx org.apache.servicecomb provider-springmvc org.apache.servicecomb provider-jaxrs org.apache.servicecomb provider-pojo org.apache.servicecomb handler-loadbalance org.hibernate.validator hibernate-validator org.apache.servicecomb foundation-test-scaffolding test ================================================ FILE: spring-boot/spring-boot-starters/pom.xml ================================================ java-chassis-spring-boot org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 java-chassis-spring-boot-starters Java Chassis::Spring Boot Starter::Parent pom java-chassis-spring-boot-starter-servlet java-chassis-spring-boot-starter-standalone ================================================ FILE: swagger/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default swagger Java Chassis::Swagger pom swagger-generator swagger-invocation ================================================ FILE: swagger/swagger-generator/generator-core/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-generator 3.4.0-SNAPSHOT swagger-generator-core Java Chassis::Swagger::Generator::Core jakarta.validation jakarta.validation-api io.swagger.core.v3 swagger-core-jakarta org.apache.servicecomb foundation-config jakarta.servlet jakarta.servlet-api org.reactivestreams reactive-streams org.mockito mockito-inline test org.junit.jupiter junit-jupiter-api provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding org.springframework spring-web test ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.base.DynamicEnum; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverters; import io.swagger.v3.core.converter.ResolvedSchema; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.servers.Server; import jakarta.servlet.http.Part; @SuppressWarnings("rawtypes") public final class SwaggerUtils { private SwaggerUtils() { } /** * Only ones servers and contains only base path. */ public static String getBasePath(OpenAPI swagger) { if (swagger.getServers() == null || swagger.getServers().size() == 0) { return null; } return swagger.getServers().get(0).getUrl(); } /** * Only ones servers and contains only base path. */ public static void setBasePath(OpenAPI swagger, String basePath) { if (swagger.getServers() == null) { swagger.setServers(new ArrayList<>()); } if (swagger.getServers().size() == 0) { swagger.getServers().add(new Server()); } swagger.getServers().get(0).setUrl(basePath); } public static String swaggerToString(OpenAPI swagger) { try { return Yaml.mapper().writeValueAsString(swagger); } catch (Throwable e) { throw new ServiceCombException("Convert swagger to string failed, ", e); } } public static OpenAPI parseAndValidateSwagger(URL url) { try { String swaggerContent = IOUtils.toString(url, StandardCharsets.UTF_8); OpenAPI result = internalParseSwagger(swaggerContent); validateSwagger(result); return result; } catch (Throwable e) { throw new ServiceCombException("Parse swagger from url failed, url=" + url, e); } } public static OpenAPI parseSwagger(String swaggerContent) { try { return internalParseSwagger(swaggerContent); } catch (Throwable e) { throw new ServiceCombException("Parse swagger from content failed, ", e); } } private static void validateSwagger(OpenAPI openAPI) { if (openAPI.getPaths() == null) { return; } for (PathItem pathItem : openAPI.getPaths().values()) { if (pathItem.getGet() != null) { validateOperation(pathItem.getGet()); } if (pathItem.getPost() != null) { validateOperation(pathItem.getPost()); } if (pathItem.getDelete() != null) { validateOperation(pathItem.getDelete()); } if (pathItem.getPut() != null) { validateOperation(pathItem.getPut()); } if (pathItem.getPatch() != null) { validateOperation(pathItem.getPatch()); } } } private static void validateOperation(Operation operation) { if (operation.getRequestBody() != null) { validateRequestBody(operation.getRequestBody()); } if (operation.getParameters() != null) { validateParameters(operation.getParameters()); } } private static void validateParameters(List parameters) { for (Parameter parameter : parameters) { if (parameter == null) { throw new ServiceCombException("Parameter can not be null."); } if (StringUtils.isEmpty(parameter.getName())) { throw new ServiceCombException("Parameter name is required."); } } } private static void validateRequestBody(RequestBody requestBody) { for (String contentType : requestBody.getContent().keySet()) { if (SwaggerConst.FILE_MEDIA_TYPE.equals(contentType) || SwaggerConst.FORM_MEDIA_TYPE.equals(contentType)) { continue; } if (requestBody.getExtensions() == null) { throw new ServiceCombException("Request body x-name extension is required."); } if (StringUtils.isEmpty((String) requestBody.getExtensions().get(SwaggerConst.EXT_BODY_NAME))) { throw new ServiceCombException("Request body x-name extension is required."); } break; } } private static OpenAPI internalParseSwagger(String swaggerContent) throws IOException { return Yaml.mapper().readValue(swaggerContent, OpenAPI.class); } // add descriptions to response and add a default response if absent. public static void correctResponses(Operation operation) { if (operation.getResponses() == null) { operation.setResponses(new ApiResponses()); } if (operation.getResponses().size() == 0) { operation.getResponses().addApiResponse(SwaggerConst.SUCCESS_KEY, new ApiResponse()); } for (Entry responseEntry : operation.getResponses().entrySet()) { ApiResponse response = responseEntry.getValue(); if (StringUtils.isEmpty(response.getDescription())) { response.setDescription("response of " + responseEntry.getKey()); } } } public static void correctResponses(OpenAPI swagger) { if (swagger.getPaths() == null) { return; } for (PathItem path : swagger.getPaths().values()) { for (Operation operation : path.readOperations()) { correctResponses(operation); } } } public static Schema resolveTypeSchemas(OpenAPI swagger, Type type) { ResolvedSchema resolvedSchema = ModelConverters.getInstance().resolveAsResolvedSchema( new AnnotatedType(type).resolveAsRef(true)); if (resolvedSchema == null || resolvedSchema.schema == null) { throw new IllegalArgumentException("cannot resolve type : " + type); } if (swagger.getComponents() == null) { swagger.setComponents(new Components()); } Map schemaMap = resolvedSchema.referencedSchemas; if (!CollectionUtils.isEmpty(schemaMap)) { Map componentSchemas = swagger.getComponents().getSchemas(); if (componentSchemas == null) { componentSchemas = new LinkedHashMap<>(schemaMap); } else { for (Map.Entry entry : schemaMap.entrySet()) { if (!componentSchemas.containsKey(entry.getKey())) { componentSchemas.put(entry.getKey(), entry.getValue()); } else { if (!schemaEquals(entry.getValue(), componentSchemas.get(entry.getKey()))) { throw new IllegalArgumentException("duplicate param model: " + entry.getKey()); } } } } swagger.getComponents().setSchemas(componentSchemas); } return resolvedSchema.schema; } // swagger api equals method will compare Map address(extensions) // and is not applicable for usage. public static int schemaHashCode(Schema schema) { int result = schema.getType() != null ? schema.getType().hashCode() : 0; result = result ^ (schema.getFormat() != null ? schema.getFormat().hashCode() : 0); result = result ^ (schema.getName() != null ? schema.getName().hashCode() : 0); result = result ^ (schema.get$ref() != null ? schema.get$ref().hashCode() : 0); result = result ^ (schema.getItems() != null ? schemaHashCode(schema.getItems()) : 0); result = result ^ (schema.getAdditionalProperties() != null ? schemaHashCode((Schema) schema.getAdditionalProperties()) : 0); result = result ^ (schema.getProperties() != null ? propertiesHashCode(schema.getProperties()) : 0); return result; } private static int propertiesHashCode(Map properties) { int result = 0; for (Entry entry : properties.entrySet()) { result = result ^ (entry.getKey().hashCode() ^ schemaHashCode(entry.getValue())); } return result; } // swagger api equals method will compare Map address(extensions) // and is not applicable for usage. public static boolean schemaEquals(Schema schema1, Schema schema2) { if (schema1 == null && schema2 == null) { return true; } if (schema1 == null || schema2 == null) { return false; } return StringUtils.equals(schema1.getType(), schema2.getType()) && StringUtils.equals(schema1.getFormat(), schema2.getFormat()) && StringUtils.equals(schema1.getName(), schema2.getName()) && StringUtils.equals(schema1.get$ref(), schema2.get$ref()) && schemaEquals(schema1.getItems(), schema2.getItems()) && schemaEquals((Schema) schema1.getAdditionalProperties(), (Schema) schema2.getAdditionalProperties()) && propertiesEquals(schema1.getProperties(), schema2.getProperties()) && extensionEquals(schema1.getExtensions(), schema2.getExtensions()); } private static boolean extensionEquals(Map extensions1, Map extensions2) { if (extensions1 == null && extensions2 == null) { return true; } if (extensions1 == null || extensions2 == null) { return false; } if (extensions1.size() != extensions2.size()) { return false; } boolean result = true; for (Entry item : extensions1.entrySet()) { if (!Objects.equals(item.getValue(), extensions2.get(item.getKey()))) { result = false; break; } } return result; } public static boolean propertiesEquals(Map properties1, Map properties2) { if (properties1 == null && properties2 == null) { return true; } if (properties1 == null || properties2 == null) { return false; } if (properties1.size() != properties2.size()) { return false; } boolean result = true; for (Entry item : properties1.entrySet()) { if (!schemaEquals(item.getValue(), properties2.get(item.getKey()))) { result = false; break; } } return result; } public static Schema getSchema(OpenAPI swagger, String ref) { return swagger.getComponents().getSchemas().get(ref.substring(Components.COMPONENTS_SCHEMAS_REF.length())); } public static String getSchemaName(String ref) { return ref.substring(Components.COMPONENTS_SCHEMAS_REF.length()); } public static Schema getSchema(OpenAPI swagger, Schema ref) { if (ref == null) { return null; } if (ref.get$ref() != null) { return getSchema(swagger, ref.get$ref()); } return ref; } public static boolean hasAnnotation(Class cls, Class annotation) { if (cls.getAnnotation(annotation) != null) { return true; } for (Method method : cls.getMethods()) { if (method.getAnnotation(annotation) != null) { return true; } } return false; } public static boolean isRawJsonType(RequestBody param) { if (param.getExtensions() == null) { return false; } Object rawJson = param.getExtensions().get(SwaggerConst.EXT_RAW_JSON_TYPE); if (rawJson instanceof Boolean) { return (boolean) rawJson; } return false; } public static String getClassName(Map vendorExtensions) { return getVendorExtension(vendorExtensions, SwaggerConst.EXT_JAVA_CLASS); } public static String getInterfaceName(Map vendorExtensions) { return getVendorExtension(vendorExtensions, SwaggerConst.EXT_JAVA_INTF); } @SuppressWarnings("unchecked") public static T getVendorExtension(Map vendorExtensions, String key) { if (vendorExtensions == null) { return null; } return (T) vendorExtensions.get(key); } public static boolean isBean(Type type) { if (type == null) { return false; } JavaType javaType = TypeFactory.defaultInstance().constructType(type); if (javaType.isContainerType() || javaType.isEnumType() || javaType.isTypeOrSubTypeOf(DynamicEnum.class)) { return false; } Class cls = javaType.getRawClass(); if (ClassUtils.isPrimitiveOrWrapper(cls)) { return false; } return (cls != String.class && cls != Date.class && cls != LocalDate.class && cls != LocalDateTime.class && cls != byte[].class && cls != File.class && cls != BigInteger.class && cls != BigDecimal.class && !cls.getName().equals("org.springframework.web.multipart.MultipartFile") && !Part.class.isAssignableFrom(cls)); } public static void updateProduces(Operation operation, String[] produces) { if (produces == null || produces.length == 0) { return; } if (operation.getResponses() == null) { operation.setResponses(new ApiResponses()); } if (operation.getResponses().size() == 0) { operation.getResponses().addApiResponse(SwaggerConst.SUCCESS_KEY, new ApiResponse()); } for (String produce : produces) { operation.getResponses().forEach((k, v) -> { if (v.getContent() == null) { v.setContent(new Content()); } if (v.getContent().get(produce) == null) { v.getContent().addMediaType(produce, new MediaType()); } }); } } public static boolean methodExists(PathItem pathItem, String httpMethod) { PathItem.HttpMethod method = PathItem.HttpMethod.valueOf(httpMethod); return switch (method) { case GET -> pathItem.getGet() != null; case PUT -> pathItem.getPut() != null; case POST -> pathItem.getPost() != null; case PATCH -> pathItem.getPatch() != null; case DELETE -> pathItem.getDelete() != null; default -> false; }; } public static String concatAbsolutePath(OpenAPI swagger, String operationPath) { String basePath = getBasePath(swagger); return concatPath(basePath, operationPath); } /** * Concat the two paths to an absolute path, without end of '/'. *

* e.g. "/" + "/ope" = /ope * e.g. "/prefix" + "/ope" = /prefix/ope */ public static String concatPath(String basePath, String operationPath) { return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath)) .replaceAll("/{2,}", "/"); } private static String nonNullify(String path) { return path == null ? "" : path; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings("rawtypes") public abstract class AbstractConverter implements Converter { protected abstract Map findVendorExtensions(Object def); protected abstract JavaType doConvert(OpenAPI swagger, Schema def); @Override public JavaType convert(OpenAPI swagger, Schema def) { Map vendorExtensions = findVendorExtensions(def); String canonical = SwaggerUtils.getClassName(vendorExtensions); if (StringUtils.isEmpty(canonical)) { return doConvert(swagger, def); } try { return TypeFactory.defaultInstance().constructFromCanonical(canonical); } catch (Throwable e) { return doConvert(swagger, def); } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/Converter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings("rawtypes") public interface Converter { JavaType OBJECT_JAVA_TYPE = TypeFactory.defaultInstance().constructType(Object.class); JavaType STRING_JAVA_TYPE = TypeFactory.defaultInstance().constructType(String.class); // def can be property or model // def can not be null JavaType convert(OpenAPI swagger, Schema def); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.converter.property.ArrayPropertyConverter; import org.apache.servicecomb.swagger.converter.property.MapPropertyConverter; import org.apache.servicecomb.swagger.converter.property.ObjectPropertyConverter; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import jakarta.servlet.http.Part; @SuppressWarnings("rawtypes") public final class ConverterMgr { private static final JavaType VOID_JAVA_TYPE = TypeFactory.defaultInstance().constructType(Void.class); // key is "type.format" of standard swagger data type // value is related java class private static final Map TYPE_FORMAT_MAP = new HashMap<>(); private static final Map, Converter> converterMap = new HashMap<>(); static { initTypeFormatMap(); initConverters(); } private static String genTypeFormatKey(String type, String format) { return type + ":" + (format == null ? "" : format); } private ConverterMgr() { } private static void initTypeFormatMap() { TYPE_FORMAT_MAP.put(genTypeFormatKey("boolean", ""), TypeFactory.defaultInstance().constructType(Boolean.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int32"), TypeFactory.defaultInstance().constructType(Integer.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int64"), TypeFactory.defaultInstance().constructType(Long.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", ""), TypeFactory.defaultInstance().constructType(BigInteger.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "float"), TypeFactory.defaultInstance().constructType(Float.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "double"), TypeFactory.defaultInstance().constructType(Double.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("number", ""), TypeFactory.defaultInstance().constructType(BigDecimal.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", ""), TypeFactory.defaultInstance().constructType(String.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date"), TypeFactory.defaultInstance().constructType(LocalDate.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date-time"), TypeFactory.defaultInstance().constructType(LocalDateTime.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "password"), TypeFactory.defaultInstance().constructType(String.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "byte"), TypeFactory.defaultInstance().constructType(Byte[].class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "binary"), TypeFactory.defaultInstance().constructType(Part.class)); } private static void initConverters() { converterMap.put(ArraySchema.class, new ArrayPropertyConverter()); converterMap.put(MapSchema.class, new MapPropertyConverter()); converterMap.put(ObjectSchema.class, new ObjectPropertyConverter()); converterMap.put(Schema.class, new ObjectPropertyConverter()); } public static JavaType findJavaType(String type, String format) { String key = genTypeFormatKey(type, format); return TYPE_FORMAT_MAP.get(key); } // def is null means void public static JavaType findJavaType(OpenAPI swagger, Schema def) { if (def == null) { return VOID_JAVA_TYPE; } JavaType javaType = findJavaType(def.getType(), def.getFormat()); if (javaType != null) { return javaType; } Converter converter = converterMap.get(def.getClass()); if (converter == null) { throw new Error("not support def type: " + def.getClass()); } return converter.convert(swagger, def); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/AbstractPropertyConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter.property; import java.util.Map; import org.apache.servicecomb.swagger.converter.AbstractConverter; import io.swagger.v3.oas.models.media.Schema; public abstract class AbstractPropertyConverter extends AbstractConverter { protected Map findVendorExtensions(Object def) { return ((Schema) def).getExtensions(); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter.property; import java.util.Collection; import java.util.List; import java.util.Set; import org.apache.servicecomb.swagger.converter.ConverterMgr; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings("rawtypes") public class ArrayPropertyConverter extends AbstractPropertyConverter { public static JavaType findJavaType(OpenAPI swagger, Schema itemProperty, Boolean uniqueItems) { JavaType itemJavaType = ConverterMgr.findJavaType(swagger, itemProperty); @SuppressWarnings("rawtypes") Class collectionClass = List.class; if (Boolean.TRUE.equals(uniqueItems)) { collectionClass = Set.class; } return TypeFactory.defaultInstance().constructCollectionType(collectionClass, itemJavaType); } @Override public JavaType doConvert(OpenAPI swagger, Schema property) { ArraySchema arrayProperty = (ArraySchema) property; return findJavaType(swagger, arrayProperty.getItems(), arrayProperty.getUniqueItems()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/MapPropertyConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter.property; import java.util.Map; import org.apache.servicecomb.swagger.converter.ConverterMgr; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings("rawtypes") public class MapPropertyConverter extends AbstractPropertyConverter { @Override public JavaType doConvert(OpenAPI swagger, Schema property) { MapSchema mapProperty = (MapSchema) property; Object valueProperty = mapProperty.getAdditionalProperties(); if (valueProperty instanceof Boolean) { return TypeFactory.defaultInstance().constructType(Boolean.class); } return findJavaType(swagger, (Schema) valueProperty); } public static JavaType findJavaType(OpenAPI swagger, Schema valueProperty) { JavaType valueJavaType = ConverterMgr.findJavaType(swagger, valueProperty); return TypeFactory.defaultInstance().constructMapType(Map.class, STRING_JAVA_TYPE, valueJavaType); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.converter.property; import java.util.Map; import org.apache.servicecomb.swagger.converter.AbstractConverter; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings("rawtypes") public class ObjectPropertyConverter extends AbstractConverter { @Override protected Map findVendorExtensions(Object def) { return ((Schema) def).getExtensions(); } @Override protected JavaType doConvert(OpenAPI swagger, Schema def) { return OBJECT_JAVA_TYPE; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ConcreteTypeRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend; import java.lang.reflect.Type; import java.util.Set; public interface ConcreteTypeRegister { void register(Set types); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/DefaultModelResolveObjectMapperProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend; import org.apache.servicecomb.swagger.extend.module.EnumModuleExt; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import io.swagger.v3.core.util.Json; public class DefaultModelResolveObjectMapperProvider implements ModelResolveObjectMapperProvider { @Override public int getOrder() { return 100; } @Override public ObjectMapper getMapper() { ObjectMapper mapper = Json.mapper(); mapper.registerModule(new EnumModuleExt()); mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, false); return mapper; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolveObjectMapperProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend; import com.fasterxml.jackson.databind.ObjectMapper; /** * Implement this interface to create custom {@link ObjectMapper} for Swagger schema generation. */ public interface ModelResolveObjectMapperProvider { /** * @return order The smaller value it is, the higher priority this provider has. */ default int getOrder() { return 0; } /** * @return the ObjectMapper, which is used by Swagger to introspect the param types and generate property collections. */ ObjectMapper getMapper(); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend; import java.lang.reflect.Type; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.base.DynamicEnum; import org.apache.servicecomb.foundation.common.base.EnumUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.extend.property.creator.ByteArrayPropertyCreator; import org.apache.servicecomb.swagger.extend.property.creator.BytePropertyCreator; import org.apache.servicecomb.swagger.extend.property.creator.InputStreamPropertyCreator; import org.apache.servicecomb.swagger.extend.property.creator.PartPropertyCreator; import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator; import org.apache.servicecomb.swagger.generator.SwaggerConst; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverterContext; import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.oas.models.media.NumberSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; /** * Customize swagger model converters to support: * * 1. byte and byte[] related types * 2. stream related types * 3. jason mapper customization * 4. add x-java-class to model */ @SuppressWarnings({"unchecked", "rawtypes"}) public class ModelResolverExt extends ModelResolver { private final Map propertyCreatorMap = new HashMap<>(); private final Set concreteInterfaces = new HashSet<>(); public ModelResolverExt() { super(findMapper()); addPropertyCreator(new BytePropertyCreator()); addPropertyCreator(new ByteArrayPropertyCreator()); addPropertyCreator(new InputStreamPropertyCreator()); addPropertyCreator(new PartPropertyCreator()); SPIServiceUtils.getAllService(PropertyCreator.class) .forEach(this::addPropertyCreator); SPIServiceUtils.getAllService(ConcreteTypeRegister.class) .forEach(r -> r.register(concreteInterfaces)); } private static ObjectMapper findMapper() { ModelResolveObjectMapperProvider objectMapperProvider = SPIServiceUtils .getPriorityHighestService(ModelResolveObjectMapperProvider.class); if (null == objectMapperProvider) { objectMapperProvider = new DefaultModelResolveObjectMapperProvider(); } return objectMapperProvider.getMapper(); } private void addPropertyCreator(PropertyCreator creator) { for (Class cls : creator.classes()) { propertyCreatorMap.put(cls, creator); propertyCreatorMap.put(TypeFactory.defaultInstance().constructType(cls), creator); } } @Override public Schema resolve(AnnotatedType propType, ModelConverterContext context, Iterator next) { PropertyCreator creator = propertyCreatorMap.get(propType.getType()); if (creator != null) { return creator.createProperty(); } if (EnumUtils.isDynamicEnum(propType.getType())) { return resolveDynamicEnum(TypeFactory.defaultInstance().constructType(propType.getType())); } Schema result = super.resolve(propType, context, next); if (!StringUtils.isEmpty(result.get$ref())) { Schema referencedSchema = context.getDefinedModels() != null ? context.getDefinedModels().get(SwaggerUtils.getSchemaName(result.get$ref())) : null; if (referencedSchema != null) { if (referencedSchema.getExtensions() == null) { referencedSchema.setExtensions(new HashMap<>()); } if (propType.getType() instanceof JavaType) { referencedSchema.getExtensions().put(SwaggerConst.EXT_JAVA_CLASS, ((JavaType) propType.getType()).toCanonical()); } else { referencedSchema.getExtensions().put(SwaggerConst.EXT_JAVA_CLASS, TypeFactory.defaultInstance().constructType(propType.getType()).toCanonical()); } } } return result; } private Schema resolveDynamicEnum(JavaType propType) { Class enumClass = propType.getRawClass(); Class enumValueClass = propType.findTypeParameters(DynamicEnum.class)[0].getRawClass(); Schema property = PrimitiveType.createProperty(enumValueClass); if (property instanceof StringSchema) { List enums = SwaggerEnum.DYNAMIC.readEnumValues(enumClass); property.setEnum(enums); } if (property instanceof NumberSchema) { List enums = SwaggerEnum.DYNAMIC.readEnumValues(enumClass); property.setEnum(enums); } return property; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/SwaggerEnum.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend; import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.base.DynamicEnum; import org.apache.servicecomb.foundation.common.base.EnumUtils; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.annotations.Parameter; public enum SwaggerEnum { JDK { @SuppressWarnings({"unchecked", "deprecation"}) @Override protected T readEnumValue(Field enumField) { Enum enumValue = EnumUtils.readEnum(enumField); return (T) Json.mapper().getSerializationConfig().getAnnotationIntrospector().findEnumValue(enumValue); } }, DYNAMIC { @Override protected T readEnumValue(Field enumField) { DynamicEnum enumValue = EnumUtils.readEnum(enumField); return enumValue.getValue(); } }; public String findPropertyDescription(Class enumClass, Annotation[] annotations) { StringBuilder sb = new StringBuilder(); String propertyDescription = readDescription(annotations, null); if (StringUtils.isNotEmpty(propertyDescription)) { appendLine(sb, propertyDescription); } EnumUtils.findEnumFields(enumClass).forEach(enumField -> { Object enumValue = readEnumValue(enumField); String description = readDescription(enumField.getAnnotations(), ""); appendLine(sb, "- %s: %s", enumValue, description); }); return sb.toString(); } public List readEnumValues(Class enumClass) { return EnumUtils.findEnumFields(enumClass) .map(this::readEnumValue) .collect(Collectors.toList()); } protected abstract T readEnumValue(Field enumField); @SuppressWarnings("unchecked") private T findAnnotation(Annotation[] annotations, Class cls) { if (annotations == null) { return null; } return Arrays.stream(annotations) .filter(annotation -> cls.isAssignableFrom(annotation.getClass())) .map(annotation -> (T) annotation) .findAny() .orElse(null); } private String readDescription(Annotation[] annotations, String defaultDescription) { Parameter apiParam = findAnnotation(annotations, Parameter.class); if (apiParam != null && StringUtils.isNotEmpty(apiParam.description())) { return apiParam.description(); } return defaultDescription; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/annotations/RawJsonRequestBody.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RawJsonRequestBody { // aliasFor "name" String value() default ""; // aliasFor "value" String name() default ""; boolean required() default true; } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.introspector; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.base.EnumUtils; import org.apache.servicecomb.swagger.extend.SwaggerEnum; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.introspect.Annotated; import io.swagger.v3.core.jackson.SwaggerAnnotationIntrospector; import java.util.Objects; public class JsonPropertyIntrospector extends SwaggerAnnotationIntrospector { private static final long serialVersionUID = 4157263023893695762L; @SuppressWarnings("deprecation") @Override public String findEnumValue(Enum value) { try { JsonProperty annotation = value.getClass().getField(value.name()).getAnnotation(JsonProperty.class); if (null == annotation || StringUtils.isEmpty(annotation.value())) { return super.findEnumValue(value); } return annotation.value(); } catch (NoSuchFieldException e) { return super.findEnumValue(value); } } @Override public String findPropertyDescription(Annotated annotated) { Class enumClass = annotated.getRawType(); if (enumClass.isEnum() && Objects.nonNull(annotated.getAnnotated())) { return SwaggerEnum.JDK.findPropertyDescription(enumClass, annotated.getAnnotated().getAnnotations()); } if (EnumUtils.isDynamicEnum(enumClass) && Objects.nonNull(annotated.getAnnotated())) { return SwaggerEnum.DYNAMIC.findPropertyDescription(enumClass, annotated.getAnnotated().getAnnotations()); } return super.findPropertyDescription(annotated); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExt.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.module; import org.apache.servicecomb.swagger.extend.introspector.JsonPropertyIntrospector; import com.fasterxml.jackson.databind.module.SimpleModule; public class EnumModuleExt extends SimpleModule { private static final long serialVersionUID = 2934449381601447264L; public EnumModuleExt() { super("SCB-swagger-enum"); } @Override public void setupModule(SetupContext context) { context.insertAnnotationIntrospector(new JsonPropertyIntrospector()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.property.creator; import io.swagger.v3.oas.models.media.ByteArraySchema; import io.swagger.v3.oas.models.media.Schema; /** * Swagger core will generate byte array to type=array, this is not correct. * In Open API, byte array should use type=string,format=byte. */ @SuppressWarnings({"rawtypes"}) public class ByteArrayPropertyCreator implements PropertyCreator { private final Class[] classes = {Byte[].class, byte[].class}; @Override public Schema createProperty() { return new ByteArraySchema(); } @Override public Class[] classes() { return classes; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.property.creator; import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.Schema; /** * Swagger core will generate byte to type=string,format=byte, this is not correct. * In Open API, type=string,format=byte is for byte array. */ @SuppressWarnings({"rawtypes"}) public class BytePropertyCreator implements PropertyCreator { private final Class[] classes = {Byte.class, byte.class}; @Override public Schema createProperty() { return new IntegerSchema(); } @Override public Class[] classes() { return classes; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.property.creator; import java.io.InputStream; import io.swagger.v3.oas.models.media.ByteArraySchema; import io.swagger.v3.oas.models.media.Schema; public class InputStreamPropertyCreator implements PropertyCreator { private final Class[] classes = {InputStream.class}; @Override public Schema createProperty() { return new ByteArraySchema(); } @Override public Class[] classes() { return classes; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.property.creator; import io.swagger.v3.oas.models.media.FileSchema; import io.swagger.v3.oas.models.media.Schema; import jakarta.servlet.http.Part; public class PartPropertyCreator implements PropertyCreator { private final Class[] classes = {Part.class}; @Override public Schema createProperty() { return new FileSchema(); } @Override public Class[] classes() { return classes; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.property.creator; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings({"rawtypes"}) public interface PropertyCreator { Schema createProperty(); Class[] classes(); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.reflect.Type; public interface ClassAnnotationProcessor { Type getProcessType(); void process(SwaggerGenerator swaggerGenerator, ANNOTATION annotation); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/MethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.reflect.Type; public interface MethodAnnotationProcessor { Type getProcessType(); void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ANNOTATION annotation); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import org.apache.servicecomb.swagger.generator.core.OperationGeneratorContext; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; public interface OperationGenerator { OpenAPI getSwagger(); Operation getOperation(); void setHttpMethod(String httpMethod); String getHttpMethod(); void addOperationToSwagger(); void setPath(String value); OperationGeneratorContext getOperationGeneratorContext(); /** * Used to check if one of operation has form parameter */ boolean isForm(); /** * Used to check if one of operation form parameter is binary */ boolean isBinary(); /** * * Used to check if this operation is websocket */ boolean isWebsocket(); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.reflect.Type; public interface ParameterAnnotationProcessor { Type getProcessType(); /** * Get parameter name from annotation. This method is used in both * swagger generation and invocation. In invocation, need to find the * swagger parameter name of the method parameter. */ String getParameterName(ANNOTATION annotation); void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, ANNOTATION annotation); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.annotation.Annotation; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.swagger.generator.core.ParameterGeneratorContext; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.CookieParameter; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.PathParameter; import io.swagger.v3.oas.models.parameters.QueryParameter; import io.swagger.v3.oas.models.parameters.RequestBody; public class ParameterGenerator { private final List annotations; private final OperationGenerator operationGenerator; private final ParameterGeneratorContext parameterGeneratorContext; /** * Used for pojo wrap parameter, while parameter type is null. */ public ParameterGenerator(OperationGenerator operationGenerator, String parameterName, Schema schema) { this.operationGenerator = operationGenerator; this.parameterGeneratorContext = new ParameterGeneratorContext(operationGenerator.getOperationGeneratorContext()); this.parameterGeneratorContext.setParameterName(parameterName); this.parameterGeneratorContext.setSchema(schema); this.annotations = Collections.emptyList(); } /** * Used for @BeanParam like parameters, while extract JavaType of the bean parameter type. */ public ParameterGenerator(OperationGenerator operationGenerator, Map> methodAnnotationMap, String parameterName, Annotation[] parameterAnnotations, JavaType genericType) { this.operationGenerator = operationGenerator; this.annotations = SwaggerGeneratorUtils.collectParameterAnnotations(parameterAnnotations, methodAnnotationMap, parameterName); this.parameterGeneratorContext = new ParameterGeneratorContext(operationGenerator.getOperationGeneratorContext()); this.parameterGeneratorContext.setParameterName(parameterName); this.parameterGeneratorContext.setParameterType(genericType); } /** * Used for normal method parameter initialization, while extract JavaType from method parameter. */ public ParameterGenerator(OperationGenerator operationGenerator, Map> methodAnnotationMap, java.lang.reflect.Parameter methodParameter, JavaType genericType) { this(operationGenerator, methodAnnotationMap, methodParameter.isNamePresent() ? methodParameter.getName() : null, methodParameter.getAnnotations(), genericType); } /** * Used for annotation defined parameter, while initial parameter type is null * and will extract JavaType annotation processors. */ public ParameterGenerator(OperationGenerator operationGenerator, String parameterName, List annotations) { this.operationGenerator = operationGenerator; this.parameterGeneratorContext = new ParameterGeneratorContext(operationGenerator.getOperationGeneratorContext()); this.parameterGeneratorContext.setParameterName(parameterName); this.annotations = annotations; } public ParameterGeneratorContext getParameterGeneratorContext() { return this.parameterGeneratorContext; } public List getAnnotations() { return annotations; } public JavaType getGenericType() { return this.parameterGeneratorContext.getParameterType(); } public HttpParameterType getHttpParameterType() { return this.parameterGeneratorContext.getHttpParameterType(); } public void setHttpParameterType(HttpParameterType httpParameterType) { this.parameterGeneratorContext.setHttpParameterType(httpParameterType); } public boolean isForm() { return parameterGeneratorContext.isForm(); } public boolean isBinary() { return parameterGeneratorContext.isBinary(); } public void generate() { this.parameterGeneratorContext.updateConsumes( this.operationGenerator.isForm(), this.operationGenerator.isBinary(), this.operationGenerator.isWebsocket()); if (this.parameterGeneratorContext.getHttpParameterType() == HttpParameterType.BODY) { if (parameterGeneratorContext.getSupportedConsumes().size() == 0) { throw new IllegalArgumentException("Consumes not provided for BODY parameter, or is empty " + "by annotations rule."); } RequestBody requestBody = new RequestBody(); requestBody.setRequired(parameterGeneratorContext.getRequired()); Map extensions = new HashMap<>(); extensions.put(SwaggerConst.EXT_BODY_NAME, parameterGeneratorContext.getParameterName()); if (parameterGeneratorContext.getRawJson() != null) { extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, parameterGeneratorContext.getRawJson()); } requestBody.setExtensions(extensions); requestBody.setContent(new Content()); for (String media : parameterGeneratorContext.getSupportedConsumes()) { MediaType mediaType = new MediaType(); mediaType.setSchema(parameterGeneratorContext.getSchema()); requestBody.getContent().addMediaType(media, mediaType); } this.operationGenerator.getOperation().setRequestBody(requestBody); return; } if (this.parameterGeneratorContext.getHttpParameterType() == HttpParameterType.FORM) { if (parameterGeneratorContext.getSupportedConsumes().size() == 0) { throw new IllegalArgumentException("Consumes not provided for FORM parameter, or is empty " + "by annotations rule."); } RequestBody requestBody = this.operationGenerator.getOperation().getRequestBody(); if (requestBody == null) { requestBody = new RequestBody(); requestBody.setContent(new Content()); this.operationGenerator.getOperation().setRequestBody(requestBody); } for (String media : parameterGeneratorContext.getSupportedConsumes()) { MediaType mediaType = requestBody.getContent().get(media); if (mediaType == null) { mediaType = new MediaType(); mediaType.setSchema(new ObjectSchema()); requestBody.getContent().addMediaType(media, mediaType); } mediaType.getSchema().addProperty(parameterGeneratorContext.getParameterName(), parameterGeneratorContext.getSchema()); } return; } Parameter parameter; switch (this.parameterGeneratorContext.getHttpParameterType()) { case PATH -> parameter = new PathParameter(); case QUERY -> parameter = new QueryParameter(); case HEADER -> parameter = new HeaderParameter(); case COOKIE -> parameter = new CookieParameter(); default -> throw new IllegalStateException("not support httpParameterType " + this.parameterGeneratorContext.getHttpParameterType()); } parameter.setName(parameterGeneratorContext.getParameterName()); parameter.setSchema(parameterGeneratorContext.getSchema()); parameter.setRequired(parameterGeneratorContext.getRequired()); parameter.setExplode(parameterGeneratorContext.getExplode()); this.operationGenerator.getOperation().addParametersItem(parameter); // validations and other annotations supported by swagger default if (parameterGeneratorContext.getParameterType() != null) { io.swagger.v3.core.util.ParameterProcessor.applyAnnotations(parameter, parameterGeneratorContext.getParameterType(), annotations, operationGenerator.getSwagger().getComponents(), null, null, null); } // spring mvc DefaultValue annotation not processed by swagger api. if (parameterGeneratorContext.getDefaultValue() != null) { parameter.getSchema().setDefault(parameterGeneratorContext.getDefaultValue()); } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import com.fasterxml.jackson.databind.JavaType; public interface ParameterTypeProcessor { JavaType getProcessType(); void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.reflect.Type; import io.swagger.v3.oas.models.media.Schema; public interface ResponseTypeProcessor { Type getProcessType(); /** * @return if genericResponseType is CompletableFuture<String>, then return String */ Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType); default Type extractResponseType(Type genericResponseType) { return extractResponseType(null, null, genericResponseType); } Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; public final class SwaggerConst { private SwaggerConst() { } public static final String SUCCESS_KEY = String.valueOf(Response.Status.OK.getStatusCode()); public static final String DEFAULT_MEDIA_TYPE = MediaType.APPLICATION_JSON; public static final String FORM_MEDIA_TYPE = MediaType.APPLICATION_FORM_URLENCODED; public static final String FILE_MEDIA_TYPE = MediaType.MULTIPART_FORM_DATA; public static final String PROTOBUF_TYPE = "application/protobuf"; public static final String WEBSOCKET_TYPE = "application/websocket"; public static final String TAG_WEBSOCKET = "websocket"; public static final String EXT_JAVA_INTF = "x-java-interface"; public static final String EXT_JAVA_CLASS = "x-java-class"; public static final String EXT_RAW_JSON_TYPE = "x-raw-json"; public static final String EXT_JSON_VIEW = "x-json-view"; public static final String EXT_BODY_NAME = "x-name"; } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerContextRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.reflect.Type; public interface SwaggerContextRegister { Type getContextType(); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.reflect.Method; import java.util.List; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.core.SwaggerGeneratorContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.oas.models.OpenAPI; public interface SwaggerGenerator { Logger LOGGER = LoggerFactory.getLogger(SwaggerGenerator.class); static OpenAPI generate(Class cls) { return create(cls).generate(); } static SwaggerGenerator create(Class cls) { List factories = SPIServiceUtils.getOrLoadSortedService(SwaggerGeneratorFactory.class); for (SwaggerGeneratorFactory factory : factories) { if (factory.canProcess(cls)) { LOGGER.info("select [{}] for [{}] to generate schema.", factory.getClass().getName(), cls.getName()); return factory.create(cls); } } throw new IllegalStateException("impossible, must be bug. can not generate swagger for " + cls.getName()); } SwaggerGeneratorContext getSwaggerGeneratorContext(); /** * base path contains application server context-path, and schema base path. */ void setBasePath(String basePath); void scanClassAnnotation(); OpenAPI generate(); Class getClazz(); OpenAPI getOpenAPI(); void setHttpMethod(String httpMethod); void replaceMethodWhiteList(String... methodNames); T createOperationGenerator(Method method); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import org.apache.servicecomb.foundation.common.utils.SPIOrder; public interface SwaggerGeneratorFactory extends SPIOrder { boolean canProcess(Class cls); SwaggerGenerator create(Class cls); } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; import java.lang.annotation.Annotation; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.core.util.Json; @SuppressWarnings("rawtypes") public final class SwaggerGeneratorUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerGeneratorUtils.class); // all static fields load from SPI and stateless private static final Set contextTypes = SPIServiceUtils.getOrLoadSortedService(SwaggerContextRegister.class) .stream() .map(swaggerContextRegister -> TypeFactory.defaultInstance() .constructType(swaggerContextRegister.getContextType())) .collect(Collectors.toSet()); private static final Map> classAnnotationProcessors = new HashMap<>(); private static final Map> methodAnnotationProcessors = new HashMap<>(); private static final Map responseTypeProcessors = new HashMap<>(); private static final DefaultResponseTypeProcessor defaultResponseTypeProcessor = new DefaultResponseTypeProcessor(); private static final List parameterAnnotationProcessors = SPIServiceUtils .getOrLoadSortedService(ParameterAnnotationProcessor.class); private static final List parameterTypeProcessors = SPIServiceUtils .getOrLoadSortedService(ParameterTypeProcessor.class); static { // low order value has high priority for (ClassAnnotationProcessor processor : SPIServiceUtils .getOrLoadSortedService(ClassAnnotationProcessor.class)) { if (classAnnotationProcessors.putIfAbsent(processor.getProcessType(), processor) != null) { LOGGER.info("ignore duplicated ClassAnnotationProcessor, type={}, processor={}.", processor.getProcessType().getTypeName(), processor.getClass().getName()); } } for (MethodAnnotationProcessor processor : SPIServiceUtils .getOrLoadSortedService(MethodAnnotationProcessor.class)) { if (methodAnnotationProcessors.putIfAbsent(processor.getProcessType(), processor) != null) { LOGGER.info("ignore duplicated MethodAnnotationProcessor, type={}, processor={}.", processor.getProcessType().getTypeName(), processor.getClass().getName()); } } for (ResponseTypeProcessor processor : SPIServiceUtils.getOrLoadSortedService(ResponseTypeProcessor.class)) { if (responseTypeProcessors.putIfAbsent(processor.getProcessType(), processor) != null) { LOGGER.info("ignore duplicated ResponseTypeProcessor, type={}, processor={}.", processor.getProcessType().getTypeName(), processor.getClass().getName()); } } List modules = SPIServiceUtils.getOrLoadSortedService(Module.class); Json.mapper().registerModules(modules.toArray(new Module[0])); } private SwaggerGeneratorUtils() { } @SuppressWarnings("unchecked") public static ClassAnnotationProcessor findClassAnnotationProcessor(Type type) { return (ClassAnnotationProcessor) classAnnotationProcessors.get(type); } @SuppressWarnings("unchecked") public static MethodAnnotationProcessor findMethodAnnotationProcessor(Type type) { return (MethodAnnotationProcessor) methodAnnotationProcessors.get(type); } @SuppressWarnings("unchecked") public static ParameterAnnotationProcessor findParameterAnnotationProcessor(Type type) { for (ParameterAnnotationProcessor processor : parameterAnnotationProcessors) { if (processor.getProcessType() == type) { return processor; } } return null; } public static ParameterTypeProcessor findParameterTypeProcessor(Type type) { for (ParameterTypeProcessor processor : parameterTypeProcessors) { if (processor.getProcessType() == type) { return processor; } } return null; } public static ResponseTypeProcessor findResponseTypeProcessor(Type type) { ResponseTypeProcessor processor = responseTypeProcessors.get(type); if (processor != null) { return processor; } if (type instanceof ParameterizedType) { return responseTypeProcessors.getOrDefault(((ParameterizedType) type).getRawType(), defaultResponseTypeProcessor); } return defaultResponseTypeProcessor; } public static boolean isContextParameter(JavaType type) { return contextTypes.contains(type); } public static Annotation[] collectAnnotations(BeanPropertyDefinition propertyDefinition) { List annotations = new ArrayList<>(); if (propertyDefinition.getField() != null) { Collections.addAll(annotations, propertyDefinition.getField().getAnnotated().getAnnotations()); } if (propertyDefinition.getGetter() != null) { Collections.addAll(annotations, propertyDefinition.getGetter().getAnnotated().getAnnotations()); } if (propertyDefinition.getSetter() != null) { Collections.addAll(annotations, propertyDefinition.getSetter().getAnnotated().getAnnotations()); } return annotations.toArray(new Annotation[0]); } public static String collectParameterName(java.lang.reflect.Parameter methodParameter) { return collectParameterName(methodParameter.getDeclaringExecutable(), methodParameter.getAnnotations(), methodParameter.isNamePresent() ? methodParameter.getName() : null); } public static String collectParameterName(Method method, BeanPropertyDefinition propertyDefinition) { Annotation[] annotations = collectAnnotations(propertyDefinition); return collectParameterName(method, annotations, propertyDefinition.getName()); } public static String collectParameterName(Executable executable, Annotation[] annotations, String defaultName) { // 1.annotations // it's ambiguous to use different name in different annotation // so we only read the first available name for (Annotation annotation : annotations) { ParameterAnnotationProcessor processor = findParameterAnnotationProcessor( annotation.annotationType()); if (processor == null) { continue; } String name = processor.getParameterName(annotation); if (StringUtils.isNotEmpty(name)) { return name; } } // 2.use signature name // ensure present parameter name if (StringUtils.isNotEmpty(defaultName)) { return defaultName; } String msg = String.format(""" parameter name is not present, method=%s:%s solution: change pom.xml, add compiler argument: -parameters, for example: org.apache.maven.plugins maven-compiler-plugin -parameters """, executable.getDeclaringClass().getName(), executable.getName()); throw new IllegalStateException(msg); } public static List collectParameterAnnotations(Annotation[] parameterAnnotations, Map> methodAnnotationMap, String parameterName) { List methodAnnotations = methodAnnotationMap.remove(parameterName); if (methodAnnotations == null) { methodAnnotations = Collections.emptyList(); } List annotations = new ArrayList<>(); Collections.addAll(annotations, parameterAnnotations); annotations.addAll(methodAnnotations); return annotations; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator; /** * A generic class to help classify implementations. Processors of swagger annotations * will implement this class. */ public abstract class SwaggerParameterAnnotationProcessor implements ParameterAnnotationProcessor { } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectAnnotations; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findMethodAnnotationProcessor; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findResponseTypeProcessor; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.isContextParameter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.inject.PlaceholderResolver; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.ParameterTypeProcessor; import org.apache.servicecomb.swagger.generator.ResponseTypeProcessor; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.type.TypeFactory; import com.google.common.reflect.TypeToken; import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.ReflectionUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.headers.Header; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import jakarta.servlet.http.HttpServletResponse; @SuppressWarnings("rawtypes") public abstract class AbstractOperationGenerator implements OperationGenerator { protected AbstractSwaggerGenerator swaggerGenerator; protected OpenAPI swagger; protected Class clazz; protected Method method; protected String httpMethod; protected List parameterGenerators = new ArrayList<>(); protected String path; protected Operation swaggerOperation; protected OperationGeneratorContext operationGeneratorContext; public AbstractOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { this.swaggerGenerator = swaggerGenerator; this.swagger = swaggerGenerator.getOpenAPI(); this.clazz = swaggerGenerator.getClazz(); this.method = method; this.httpMethod = swaggerGenerator.getHttpMethod(); operationGeneratorContext = new OperationGeneratorContext(swaggerGenerator.getSwaggerGeneratorContext()); swaggerOperation = new Operation(); } @Override public OperationGeneratorContext getOperationGeneratorContext() { return operationGeneratorContext; } @Override public void setHttpMethod(String httpMethod) { if (StringUtils.isEmpty(httpMethod)) { return; } this.httpMethod = httpMethod.toUpperCase(Locale.US); } @Override public OpenAPI getSwagger() { return this.swagger; } @Override public String getHttpMethod() { return httpMethod; } @Override public Operation getOperation() { return swaggerOperation; } public String getOperationId() { return swaggerOperation.getOperationId(); } @Override public void setPath(String path) { path = new PlaceholderResolver().replaceFirst(path); if (!path.startsWith("/")) { path = "/" + path; } this.path = path; } public void generate() { scanMethodAnnotation(); scanMethodParameters(); scanResponse(); correctOperation(); } protected void scanMethodAnnotation() { for (Annotation annotation : Arrays.stream(method.getAnnotations()) .sorted(Comparator.comparing(a -> a.annotationType().getSimpleName())) .collect(Collectors.toList())) { MethodAnnotationProcessor processor = findMethodAnnotationProcessor(annotation.annotationType()); if (processor == null) { continue; } processor.process(swaggerGenerator, this, annotation); } if (StringUtils.isEmpty(swaggerOperation.getOperationId())) { swaggerOperation.setOperationId(MethodUtils.findSwaggerMethodName(method)); } } protected void scanMethodParameters() { // init generators initParameterGenerators(); // scan annotations and types Set names = new HashSet<>(); int bodyCount = 0; for (ParameterGenerator parameterGenerator : parameterGenerators) { scanMethodParameter(parameterGenerator); if (!names.add(parameterGenerator.getParameterGeneratorContext().getParameterName())) { throw new IllegalArgumentException( String.format("not support duplicated parameter, name=%s.", parameterGenerator.getParameterGeneratorContext().getParameterName())); } if (parameterGenerator.getHttpParameterType() == HttpParameterType.BODY) { if (bodyCount > 0) { throw new IllegalArgumentException(String.format("Defined %d body parameter.", bodyCount)); } bodyCount++; } } // generate for (ParameterGenerator parameterGenerator : parameterGenerators) { parameterGenerator.generate(); } } protected void initParameterGenerators() { // 1.group method annotations by parameter name // key is parameter name Map> methodAnnotationMap = initMethodAnnotationByParameterName(); // 2.create ParameterGenerators by method parameters, merge annotations with method annotations initMethodParameterGenerators(methodAnnotationMap); // 3.create ParameterGenerators remains method annotations initRemainMethodAnnotationsParameterGenerators(methodAnnotationMap); } protected void initMethodParameterGenerators(Map> methodAnnotationMap) { for (java.lang.reflect.Parameter methodParameter : method.getParameters()) { Type genericType = TypeToken.of(clazz) .resolveType(methodParameter.getParameterizedType()) .getType(); ParameterGenerator parameterGenerator = new ParameterGenerator( this, methodAnnotationMap, methodParameter, TypeFactory.defaultInstance().constructType(genericType)); validateParameter(parameterGenerator.getGenericType()); if (isContextParameter(parameterGenerator.getGenericType())) { continue; } // jaxrs: @BeanParam // springmvc: is query, and is bean type if (isAggregatedParameter(parameterGenerator, methodParameter)) { extractAggregatedParameterGenerators(methodAnnotationMap, methodParameter); continue; } parameterGenerators.add(parameterGenerator); } } protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator, java.lang.reflect.Parameter methodParameter) { return false; } protected void extractAggregatedParameterGenerators(Map> methodAnnotationMap, java.lang.reflect.Parameter methodParameter) { JavaType javaType = TypeFactory.defaultInstance().constructType(methodParameter.getParameterizedType()); BeanDescription beanDescription = Json.mapper().getSerializationConfig().introspect(javaType); for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) { if (!propertyDefinition.couldSerialize()) { continue; } Annotation[] annotations = collectAnnotations(propertyDefinition); ParameterGenerator propertyParameterGenerator = new ParameterGenerator(this, methodAnnotationMap, propertyDefinition.getName(), annotations, propertyDefinition.getPrimaryType()); parameterGenerators.add(propertyParameterGenerator); } } protected void initRemainMethodAnnotationsParameterGenerators(Map> methodAnnotationMap) { for (Entry> entry : methodAnnotationMap.entrySet()) { ParameterGenerator parameterGenerator = new ParameterGenerator(this, entry.getKey(), entry.getValue()); parameterGenerators.add(parameterGenerator); } } private Map> initMethodAnnotationByParameterName() { Map> methodAnnotations = new LinkedHashMap<>(); for (Annotation annotation : method.getAnnotations()) { if (annotation instanceof io.swagger.v3.oas.annotations.Parameters) { for (io.swagger.v3.oas.annotations.Parameter apiImplicitParam : ((io.swagger.v3.oas.annotations.Parameters) annotation).value()) { addMethodAnnotationByParameterName(methodAnnotations, apiImplicitParam.name(), apiImplicitParam); } continue; } if (annotation instanceof io.swagger.v3.oas.annotations.Parameter) { addMethodAnnotationByParameterName(methodAnnotations, ((io.swagger.v3.oas.annotations.Parameter) annotation).name(), annotation); } } return methodAnnotations; } private void addMethodAnnotationByParameterName(Map> methodAnnotations, String name, Annotation annotation) { if (StringUtils.isEmpty(name)) { throw new IllegalStateException(String.format("%s.name should not be empty. method=%s:%s", annotation.annotationType().getSimpleName(), method.getDeclaringClass().getName(), method.getName())); } methodAnnotations.computeIfAbsent(name, n -> new ArrayList<>()) .add(annotation); } protected void validateParameter(JavaType type) { if (type.isTypeOrSubTypeOf(HttpServletResponse.class)) { // not support, log the reason throw new IllegalStateException( "all input/output of ServiceComb operation are models, not allow to use HttpServletResponse."); } } protected void scanMethodParameter(ParameterGenerator parameterGenerator) { for (Annotation annotation : parameterGenerator.getAnnotations()) { ParameterAnnotationProcessor processor = SwaggerGeneratorUtils .findParameterAnnotationProcessor(annotation.annotationType()); if (processor != null) { processor.process(this.swaggerGenerator, this, parameterGenerator, annotation); } } Schema schema = parameterGenerator.getParameterGeneratorContext().getSchema(); if (schema == null) { JavaType parameterType = parameterGenerator.getGenericType(); ParameterTypeProcessor processor = SwaggerGeneratorUtils.findParameterTypeProcessor(parameterType); if (processor != null) { processor.process(this.swaggerGenerator, this, parameterGenerator); } else { parameterGenerator.getParameterGeneratorContext().setSchema(SwaggerUtils.resolveTypeSchemas(this.swagger, parameterType)); } } } @Override public boolean isForm() { for (ParameterGenerator parameterGenerator : parameterGenerators) { if (parameterGenerator.isForm()) { return true; } } return false; } @Override public boolean isBinary() { for (ParameterGenerator parameterGenerator : parameterGenerators) { if (parameterGenerator.isBinary()) { return true; } } return false; } @Override public boolean isWebsocket() { return this.swaggerOperation.getTags() != null && this.swaggerOperation.getTags().contains(SwaggerConst.TAG_WEBSOCKET); } @Override public void addOperationToSwagger() { if (StringUtils.isEmpty(httpMethod)) { return; } if (swagger.getPaths() == null) { swagger.setPaths(new Paths()); } PathItem pathObj = swagger.getPaths().get(path); if (pathObj == null) { pathObj = new PathItem(); swagger.path(path, pathObj); } else if (SwaggerUtils.methodExists(pathObj, httpMethod)) { throw new IllegalStateException(String.format("Duplicate operation path detected. method=%s:%s.", method.getDeclaringClass().getName(), method.getName())); } pathObj.operation(PathItem.HttpMethod.valueOf(httpMethod), swaggerOperation); } public void correctOperation() { SwaggerUtils.correctResponses(swaggerOperation); } public void scanResponse() { operationGeneratorContext.updateProduces(); swaggerOperation.setResponses(new ApiResponses()); for (Entry> response : operationGeneratorContext.getResponses().entrySet()) { ApiResponse apiResponse = new ApiResponse(); if (StringUtils.isNotEmpty(operationGeneratorContext.getResponseDescriptions().get(response.getKey()))) { apiResponse.setDescription(operationGeneratorContext.getResponseDescriptions().get(response.getKey())); } if (operationGeneratorContext.getResponseHeaders().get(response.getKey()) != null) { operationGeneratorContext.getResponseHeaders().get(response.getKey()).forEach((k, v) -> { Header header = new Header(); header.setSchema(v); apiResponse.addHeaderObject(k, header); }); } Schema schema = response.getValue() != null ? response.getValue() : createResponseModel(); if (schema == null) { swaggerOperation.getResponses().addApiResponse(response.getKey(), apiResponse); continue; } apiResponse.setContent(new Content()); // file download using WILDCARD content-type if ("string".equals(schema.getType()) && "binary".equals(schema.getFormat())) { MediaType mediaType = new MediaType(); mediaType.setSchema(schema); apiResponse.getContent().addMediaType(jakarta.ws.rs.core.MediaType.WILDCARD, mediaType); } else { for (String produce : operationGeneratorContext.getSupportedProduces()) { MediaType mediaType = new MediaType(); mediaType.setSchema(schema); apiResponse.getContent().addMediaType(produce, mediaType); } } swaggerOperation.getResponses().addApiResponse(response.getKey(), apiResponse); } } protected Schema createResponseModel() { Type responseType = TypeToken.of(clazz) .resolveType(method.getGenericReturnType()) .getType(); if (ReflectionUtils.isVoid(responseType)) { return null; } ResponseTypeProcessor processor = findResponseTypeProcessor(responseType); return processor.process(swaggerGenerator, this, responseType); } public Method getMethod() { return method; } public List getParameterGenerators() { return parameterGenerators; } public Operation getSwaggerOperation() { return swaggerOperation; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findClassAnnotationProcessor; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.inject.PlaceholderResolver; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.servers.Server; /** *

 * support:
 * 1.pojo + swagger annotation
 *   wrap all input parameter to be fields of body
 * 2.pojo + swagger annotation + jaxrs annotation
 * 3.pojo + swagger annotation + springmvc annotation
 * 
*/ public abstract class AbstractSwaggerGenerator implements SwaggerGenerator { protected SwaggerGeneratorContext swaggerGeneratorContext = new SwaggerGeneratorContext(); protected Class cls; protected OpenAPI openAPI; // allowed to control only process some methods // empty means all methods are available protected Set methodWhiteList = new HashSet<>(); // key is operationId // to check if operationId is duplicated protected Map operationGenerators = new LinkedHashMap<>(); protected String httpMethod; public AbstractSwaggerGenerator(Class cls) { this.openAPI = new OpenAPI(); this.openAPI.components(new Components()) .paths(new Paths()) .servers(new ArrayList<>()) .info(new Info()); this.cls = cls; } public OpenAPI getOpenAPI() { return openAPI; } @Override public Class getClazz() { return cls; } public String getHttpMethod() { return httpMethod; } @Override public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod.toUpperCase(Locale.US); } @Override public SwaggerGeneratorContext getSwaggerGeneratorContext() { return swaggerGeneratorContext; } public OpenAPI generate() { LOGGER.info("generate schema from [{}]", cls); scanClassAnnotation(); scanMethods(); addOperationsToSwagger(); correctSwagger(); return openAPI; } public void scanClassAnnotation() { for (Annotation annotation : cls.getAnnotations()) { ClassAnnotationProcessor processor = findClassAnnotationProcessor(annotation.annotationType()); if (processor == null) { continue; } processor.process(this, annotation); } } /** * fill empty and required field to be default value * if can not build default value, then throw exceptions */ protected void correctSwagger() { correctBasePath(); correctInfo(); } private void correctBasePath() { if (openAPI.getServers() == null) { openAPI.setServers(new ArrayList<>()); } if (openAPI.getServers().size() <= 0) { Server server = new Server(); server.setUrl("/" + cls.getSimpleName()); openAPI.getServers().add(server); } } private void correctInfo() { Info info = openAPI.getInfo(); if (info == null) { info = new Info(); openAPI.setInfo(info); } if (StringUtils.isEmpty(info.getTitle())) { info.setTitle("swagger definition for " + cls.getName()); } if (StringUtils.isEmpty(info.getVersion())) { info.setVersion("1.0.0"); } } @Override public void replaceMethodWhiteList(String... methodNames) { methodWhiteList.clear(); if (methodNames == null || methodNames.length == 0) { return; } methodWhiteList.addAll(Arrays.asList(methodNames)); } /** * Whether this method should be processed as a swagger operation * @return true if this isn't a swagger operation; otherwise, false. */ protected boolean isSkipMethod(Method method) { if (method.getDeclaringClass().getName().equals(Object.class.getName())) { return true; } // skip static method int modifiers = method.getModifiers(); if (Modifier.isStatic(modifiers)) { return true; } // skip bridge method if (method.isBridge()) { return true; } Operation apiOperation = method.getAnnotation(Operation.class); if (apiOperation != null && apiOperation.hidden()) { return true; } if (!methodWhiteList.isEmpty()) { return !methodWhiteList.contains(MethodUtils.findSwaggerMethodName(method)); } return false; } protected void scanMethods() { List methods = MethodUtils.findSwaggerMethods(cls); for (Method method : methods) { if (isSkipMethod(method)) { continue; } AbstractOperationGenerator operationGenerator = createOperationGenerator(method); operationGenerator.setHttpMethod(httpMethod); try { operationGenerator.generate(); } catch (Throwable e) { String msg = String.format("Generate swagger operation failed, method=%s:%s, cause=%s", this.cls.getSimpleName(), method.getName(), e.getMessage()); throw new IllegalStateException(msg, e); } if (StringUtils.isEmpty(operationGenerator.httpMethod)) { throw new IllegalStateException( String.format("HttpMethod must not both be empty in class and method, method=%s:%s.", cls.getName(), method.getName())); } if (operationGenerators.putIfAbsent(operationGenerator.getOperationId(), operationGenerator) != null) { throw new IllegalStateException( String.format("OperationId must be unique. method=%s:%s.", cls.getName(), method.getName())); } } } protected void addOperationsToSwagger() { for (OperationGenerator operationGenerator : operationGenerators.values()) { operationGenerator.addOperationToSwagger(); } } @Override public void setBasePath(String basePath) { basePath = new PlaceholderResolver().replaceFirst(basePath); if (openAPI.getServers() == null) { openAPI.setServers(new ArrayList<>()); } if (openAPI.getServers().size() == 0) { openAPI.getServers().add(new Server()); } openAPI.getServers().get(0).setUrl(basePath); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGeneratorContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.swagger.generator.SwaggerConst; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.oas.models.media.Schema; public class OperationGeneratorContext extends SwaggerGeneratorContext { private final Map> responses = new HashMap<>(); private JavaType responseType; private final Map responseDescriptions = new HashMap<>(); // statusCode:headerName:headerSchema private final Map>> responseHeaders = new HashMap<>(); public OperationGeneratorContext(SwaggerGeneratorContext parent) { super(parent); updateResponse(SwaggerConst.SUCCESS_KEY, null); } public Map> getResponses() { return responses; } public void updateResponse(String code, Schema schema) { this.responses.put(code, schema); } public JavaType getResponseType() { return responseType; } public void setResponseType(JavaType responseType) { this.responseType = responseType; } public Map getResponseDescriptions() { return responseDescriptions; } public void updateResponseDescription(String code, String description) { this.responseDescriptions.put(code, description); } public Map>> getResponseHeaders() { return responseHeaders; } public void updateResponseHeader(String code, String header, Schema schema) { this.responseHeaders.computeIfAbsent(code, key -> new HashMap<>()); this.responseHeaders.get(code).put(header, schema); } public void updateProduces() { List removed = new ArrayList<>(); for (String media : supportedProduces) { if (SUPPORTED_BODY_CONTENT_TYPE.contains(media)) { continue; } removed.add(media); } supportedProduces.removeAll(removed); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.oas.models.media.Schema; import jakarta.ws.rs.core.MediaType; public class ParameterGeneratorContext extends OperationGeneratorContext { /** * Parameter type. Maybe override by annotations. And for pojo wrapped parameter, * this is null. */ private JavaType parameterType; private String parameterName; private HttpParameterType httpParameterType; private Boolean explode; private Boolean required; private Object defaultValue; private Boolean rawJson; private Schema schema; public ParameterGeneratorContext(OperationGeneratorContext parent) { super(parent); } public JavaType getParameterType() { return parameterType; } public void setParameterType(JavaType parameterType) { this.parameterType = parameterType; } public String getParameterName() { return parameterName; } public void setParameterName(String parameterName) { this.parameterName = parameterName; } public Boolean getExplode() { return explode; } public void setExplode(Boolean explode) { this.explode = explode; } public Boolean getRequired() { return required; } public void setRequired(Boolean required) { this.required = required; } public Boolean getRawJson() { return rawJson; } public void setRawJson(Boolean rawJson) { this.rawJson = rawJson; } public Schema getSchema() { return schema; } public void setSchema(Schema schema) { this.schema = schema; } public HttpParameterType getHttpParameterType() { return httpParameterType; } public void setHttpParameterType(HttpParameterType httpParameterType) { this.httpParameterType = httpParameterType; } public Object getDefaultValue() { return defaultValue; } public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; } public void updateConsumes(boolean isForm, boolean isBinary, boolean isWebSocket) { List removed = new ArrayList<>(); if (isWebSocket) { supportedConsumes.clear(); supportedConsumes.add(SwaggerConst.WEBSOCKET_TYPE); return; } if (httpParameterType == HttpParameterType.BODY) { if (isForm) { throw new IllegalArgumentException("Both form and body parameter not allowed."); } for (String media : supportedConsumes) { if (SUPPORTED_BODY_CONTENT_TYPE.contains(media)) { continue; } removed.add(media); } supportedConsumes.removeAll(removed); return; } if (httpParameterType == HttpParameterType.FORM) { for (String media : supportedConsumes) { if (!SUPPORTED_FORM_CONTENT_TYPE.contains(media)) { removed.add(media); continue; } if (isBinary && supportedConsumes.contains(MediaType.APPLICATION_FORM_URLENCODED)) { removed.add(MediaType.APPLICATION_FORM_URLENCODED); } if (!isBinary && supportedConsumes.contains(MediaType.MULTIPART_FORM_DATA)) { removed.add(MediaType.MULTIPART_FORM_DATA); } } supportedConsumes.removeAll(removed); return; } supportedConsumes.clear(); } public boolean isForm() { return httpParameterType == HttpParameterType.FORM; } public boolean isBinary() { if ("string".equals(this.schema.getType()) && "binary".equals(this.schema.getFormat())) { return true; } return "array".equals(this.schema.getType()) && "string".equals(this.schema.getItems().getType()) && "binary".equals(this.schema.getItems().getFormat()); } public boolean isObject() { return "object".equals(this.schema.getType()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.swagger.generator.SwaggerConst; import jakarta.ws.rs.core.MediaType; /** * Context information to help generate specific open api parts. */ public class SwaggerGeneratorContext { protected static final List SUPPORTED_CONTENT_TYPE = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN, MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED, SwaggerConst.WEBSOCKET_TYPE, MediaType.SERVER_SENT_EVENTS); protected static final List SUPPORTED_BODY_CONTENT_TYPE = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN); protected static final List SUPPORTED_FORM_CONTENT_TYPE = Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED); protected List supportedConsumes; protected List supportedProduces; public SwaggerGeneratorContext() { supportedConsumes = new ArrayList<>(); supportedConsumes.addAll(SUPPORTED_CONTENT_TYPE); supportedProduces = new ArrayList<>(); supportedProduces.addAll(SUPPORTED_CONTENT_TYPE); } public SwaggerGeneratorContext(SwaggerGeneratorContext parent) { supportedConsumes = new ArrayList<>(); supportedConsumes.addAll(parent.supportedConsumes); supportedProduces = new ArrayList<>(); supportedProduces.addAll(parent.supportedProduces); } public void updateProduces(List produces) { checkMediaTypeValid(produces); supportedProduces.clear(); supportedProduces.addAll(produces); } public void updateConsumes(List consumes) { checkMediaTypeValid(consumes); supportedConsumes.clear(); supportedConsumes.addAll(consumes); } public List getSupportedConsumes() { return supportedConsumes; } public List getSupportedProduces() { return supportedProduces; } private void checkMediaTypeValid(List produces) { for (String produce : produces) { if (!SUPPORTED_CONTENT_TYPE.contains(produce)) { throw new IllegalArgumentException("Not support media type " + produce); } } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/HttpParameterType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.model; import java.util.Locale; import org.apache.commons.lang3.StringUtils; import io.swagger.v3.oas.annotations.enums.ParameterIn; public enum HttpParameterType { /** io.swagger.v3.oas.annotations.enums.ParameterIn.PATH */ PATH, /** io.swagger.v3.oas.annotations.enums.ParameterIn.QUERY */ QUERY, /** io.swagger.v3.oas.annotations.enums.ParameterIn.HEADER */ HEADER, /** io.swagger.v3.oas.annotations.enums.ParameterIn.COOKIE */ COOKIE, /** request bodies: application/x-www-form-urlencoded, multipart/form-data */ FORM, /** request bodies: application/json, etc. */ BODY; public static HttpParameterType from(ParameterIn in) { return switch (in) { case COOKIE -> HttpParameterType.COOKIE; case QUERY -> HttpParameterType.QUERY; case HEADER -> HttpParameterType.HEADER; case PATH -> HttpParameterType.PATH; default -> null; }; } public static HttpParameterType parse(String value) { if (StringUtils.isEmpty(value)) { return null; } return HttpParameterType.valueOf(value.toUpperCase(Locale.US)); } public static boolean isBodyParameter(HttpParameterType type) { return BODY == type || FORM == type; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.model; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem.HttpMethod; public class SwaggerOperation { private final OpenAPI swagger; private final String path; private final HttpMethod httpMethod; private final Operation operation; public SwaggerOperation(OpenAPI swagger, String path, HttpMethod httpMethod, Operation operation) { this.swagger = swagger; this.path = path; this.httpMethod = httpMethod; this.operation = operation; } public int parameterCount() { int result = 0; if (operation.getRequestBody() != null) { result++; } if (operation.getParameters() != null) { result += operation.getParameters().size(); } return result; } public OpenAPI getSwagger() { return swagger; } public String getPath() { return path; } public HttpMethod getHttpMethod() { return httpMethod; } public Operation getOperation() { return operation; } public String getOperationId() { return operation.getOperationId(); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperations.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.model; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.PathItem.HttpMethod; import io.swagger.v3.oas.models.Paths; public class SwaggerOperations { public static SwaggerOperations generate(Class cls) { OpenAPI swagger = SwaggerGenerator.create(cls).generate(); return new SwaggerOperations(swagger); } private final OpenAPI swagger; // key is operationId private final Map operations = new HashMap<>(); public SwaggerOperations(OpenAPI swagger) { this.swagger = swagger; Paths paths = swagger.getPaths(); if (paths == null || paths.isEmpty()) { return; } for (Entry pathEntry : paths.entrySet()) { for (Entry operationEntry : pathEntry.getValue().readOperationsMap().entrySet()) { if (StringUtils.isEmpty(operationEntry.getValue().getOperationId())) { throw new IllegalStateException(String .format("OperationId can not be empty, path=%s, httpMethod=%s.", pathEntry.getKey(), operationEntry.getKey())); } SwaggerOperation swaggerOperation = new SwaggerOperation(swagger, pathEntry.getKey(), operationEntry.getKey(), operationEntry.getValue()); if (operations.putIfAbsent(operationEntry.getValue().getOperationId(), swaggerOperation) != null) { throw new IllegalStateException( "please make sure operationId is unique, duplicated operationId is " + operationEntry.getValue() .getOperationId()); } } } } public OpenAPI getSwagger() { return swagger; } public SwaggerOperation findOperation(String operationId) { return operations.get(operationId); } public Map getOperations() { return operations; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.extensions.Extension; import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.info.Contact; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.MediaType; /** * Utility class to convert from OpenAPI annotations to models. */ @SuppressWarnings({"rawtypes", "unchecked"}) public final class AnnotationUtils { private AnnotationUtils() { } public static List serversModel(Server[] servers) { if (servers == null) { return null; } return Arrays.stream(servers).map(AnnotationUtils::convertServer).collect(Collectors.toList()); } public static io.swagger.v3.oas.models.servers.Server convertServer(Server server) { io.swagger.v3.oas.models.servers.Server item = new io.swagger.v3.oas.models.servers.Server(); item.setUrl(server.url()); item.setDescription(server.description()); return item; } public static io.swagger.v3.oas.models.info.Info infoModel(Info infoAnnotation) { if (infoAnnotation == null) { return null; } io.swagger.v3.oas.models.info.Info info = new io.swagger.v3.oas.models.info.Info(); info.setTitle(infoAnnotation.title()); info.setVersion(infoAnnotation.version()); if (StringUtils.isNotEmpty(infoAnnotation.description())) { info.setDescription(infoAnnotation.description()); } if (StringUtils.isNotEmpty(infoAnnotation.termsOfService())) { info.setTermsOfService(infoAnnotation.termsOfService()); } info.setContact(contactModel(infoAnnotation.contact())); info.setLicense(licenseModel(infoAnnotation.license())); info.setExtensions(extensionsModel(infoAnnotation.extensions())); return info; } public static io.swagger.v3.oas.models.info.License licenseModel(License licenseAnnotation) { io.swagger.v3.oas.models.info.License license = new io.swagger.v3.oas.models.info.License(); if (StringUtils.isNotEmpty(licenseAnnotation.name())) { license.setName(licenseAnnotation.name()); } if (StringUtils.isNotEmpty(licenseAnnotation.url())) { license.setUrl(licenseAnnotation.url()); } if (StringUtils.isEmpty(license.getName()) && StringUtils.isEmpty(license.getUrl())) { return null; } return license; } public static io.swagger.v3.oas.models.info.Contact contactModel(Contact contactAnnotation) { io.swagger.v3.oas.models.info.Contact contact = new io.swagger.v3.oas.models.info.Contact(); if (StringUtils.isNotEmpty(contactAnnotation.name())) { contact.setName(contactAnnotation.name()); } if (StringUtils.isNotEmpty(contactAnnotation.url())) { contact.setUrl(contactAnnotation.url()); } if (StringUtils.isNotEmpty(contactAnnotation.email())) { contact.setEmail(contactAnnotation.email()); } if (StringUtils.isEmpty(contact.getName()) && StringUtils.isEmpty(contact.getUrl()) && StringUtils.isEmpty(contact.getEmail())) { return null; } return contact; } public static List tagsModel(Tag[] tagArray) { if (tagArray == null) { return null; } List tags = Arrays.stream(tagArray) .filter(t -> !t.name().isEmpty()) .map(AnnotationUtils::tagModel) .collect(Collectors.toList()); return tags.isEmpty() ? null : tags; } public static List tagsModel(String[] tagArray) { if (tagArray == null) { return null; } List tags = Arrays.stream(tagArray) .filter(t -> !t.isEmpty()) .collect(Collectors.toList()); return tags.isEmpty() ? null : tags; } public static io.swagger.v3.oas.models.tags.Tag tagModel(Tag tagAnnotation) { io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); tag.setName(tagAnnotation.name()); tag.setDescription(tagAnnotation.description()); tag.setExternalDocs(externalDocumentationModel(tagAnnotation.externalDocs())); tag.setExtensions(extensionsModel(tagAnnotation.extensions())); return tag; } public static io.swagger.v3.oas.models.ExternalDocumentation externalDocumentationModel( ExternalDocumentation externalDocs) { io.swagger.v3.oas.models.ExternalDocumentation doc = new io.swagger.v3.oas.models.ExternalDocumentation(); doc.setUrl(externalDocs.url()); doc.setDescription(externalDocs.description()); return doc; } public static Map extensionsModel(Extension[] extensions) { Map result = new HashMap<>(); Stream.of(extensions).forEach(e -> result.put(e.name(), extensionPropertiesModel(e.properties()))); return result; } public static Map extensionPropertiesModel(ExtensionProperty[] properties) { Map result = new HashMap<>(); Stream.of(properties).forEach(e -> result.put(e.name(), e.value())); return result; } public static String responseCodeModel(ApiResponse apiResponse) { if (StringUtils.isEmpty(apiResponse.responseCode())) { return "200"; } return apiResponse.responseCode(); } public static io.swagger.v3.oas.models.responses.ApiResponses apiResponsesModel(OpenAPI openAPI, ApiResponses apiResponses) { io.swagger.v3.oas.models.responses.ApiResponses result = new io.swagger.v3.oas.models.responses.ApiResponses(); result.setExtensions(extensionsModel(apiResponses.extensions())); for (ApiResponse apiResponse : apiResponses.value()) { result.addApiResponse(responseCodeModel(apiResponse), apiResponseModel(openAPI, apiResponse)); } return result; } public static io.swagger.v3.oas.models.responses.ApiResponses apiResponsesModel(OpenAPI openAPI, ApiResponse[] apiResponses) { io.swagger.v3.oas.models.responses.ApiResponses result = new io.swagger.v3.oas.models.responses.ApiResponses(); for (ApiResponse apiResponse : apiResponses) { if (result.get(responseCodeModel(apiResponse)) != null) { throw new IllegalStateException("not support too many ApiResponse with same status code"); } else { result.addApiResponse(responseCodeModel(apiResponse), apiResponseModel(openAPI, apiResponse)); } } return result; } public static io.swagger.v3.oas.models.responses.ApiResponse apiResponseModel(OpenAPI openAPI, ApiResponse apiResponse) { io.swagger.v3.oas.models.responses.ApiResponse result = new io.swagger.v3.oas.models.responses.ApiResponse(); result.setDescription(apiResponse.description()); result.setContent(contentModel(openAPI, apiResponse.content())); result.setHeaders(headersModel(openAPI, apiResponse.headers())); return result; } public static Map headersModel(OpenAPI openAPI, Header[] headers) { Map result = new HashMap<>(); for (Header header : headers) { io.swagger.v3.oas.models.headers.Header model = new io.swagger.v3.oas.models.headers.Header(); model.setDescription(header.description()); model.setSchema(schemaModel(openAPI, header.schema())); result.put(header.name(), model); } return result; } public static io.swagger.v3.oas.models.media.Content contentModel(OpenAPI openAPI, Content[] contents) { io.swagger.v3.oas.models.media.Content result = new io.swagger.v3.oas.models.media.Content(); for (io.swagger.v3.oas.annotations.media.Content content : contents) { String mediaTypeName = mediaTypeModel(content); MediaType mediaType = result.get(mediaTypeName); boolean isForm = SwaggerConst.FORM_MEDIA_TYPE.equals(mediaTypeName) || SwaggerConst.FILE_MEDIA_TYPE.equals(mediaTypeName); if (mediaType == null) { mediaType = new MediaType(); if (isForm) { io.swagger.v3.oas.models.media.Schema schema = new io.swagger.v3.oas.models.media.Schema(); schema.setProperties(new HashMap<>()); schema.addProperty(content.schema().name(), schemaModel(openAPI, content.schema(), content.examples())); mediaType.setSchema(schema); } else { mediaType.setSchema(schemaModel(openAPI, content.schema(), content.examples())); } result.addMediaType(mediaTypeName, mediaType); } else { if (isForm) { mediaType.getSchema().addProperty(content.schema().name(), schemaModel(openAPI, content.schema(), content.examples())); } else { throw new IllegalStateException("Not allowed to define duplicated content type for " + mediaTypeName); } } } return result; } public static io.swagger.v3.oas.models.parameters.RequestBody requestBodyModel(OpenAPI openAPI, RequestBody requestBody) { if (requestBody == null || isOperationDefaultRequestBody(requestBody)) { return null; } io.swagger.v3.oas.models.parameters.RequestBody result = new io.swagger.v3.oas.models.parameters.RequestBody(); result.setContent(AnnotationUtils.contentModel(openAPI, requestBody.content())); return result; } private static boolean isOperationDefaultRequestBody(RequestBody requestBody) { return "".equals(requestBody.description()) && requestBody.content().length == 0 && !requestBody.required() && requestBody.extensions().length == 0; } private static String mediaTypeModel(io.swagger.v3.oas.annotations.media.Content content) { if (StringUtils.isEmpty(content.mediaType())) { return SwaggerConst.DEFAULT_MEDIA_TYPE; } return content.mediaType(); } public static io.swagger.v3.oas.models.media.Schema schemaModel(OpenAPI openAPI, Schema schema) { if (schema.implementation() != Void.class) { io.swagger.v3.oas.models.media.Schema result = SwaggerUtils.resolveTypeSchemas(openAPI, schema.implementation()); result.setDescription(schema.description()); result.setExample(schema.example()); result.setNullable(schema.nullable()); return result; } io.swagger.v3.oas.models.media.Schema result = new io.swagger.v3.oas.models.media.Schema(); result.setType(schema.type()); result.setFormat(schema.format()); result.setDescription(schema.description()); result.setExample(schema.example()); result.setNullable(schema.nullable()); return result; } public static io.swagger.v3.oas.models.media.Schema schemaModel(OpenAPI openAPI, Schema schema, ExampleObject[] exampleObjects) { io.swagger.v3.oas.models.media.Schema result = schemaModel(openAPI, schema); List examples = new ArrayList<>(); for (ExampleObject exampleObject : exampleObjects) { examples.add(exampleObject.name() + ":" + exampleObject.value()); } result.setExamples(examples); return result; } public static io.swagger.v3.oas.models.Operation operationModel(OpenAPI openAPI, Operation apiOperationAnnotation) { io.swagger.v3.oas.models.Operation result = new io.swagger.v3.oas.models.Operation(); result.setSummary(apiOperationAnnotation.summary()); result.setDescription(apiOperationAnnotation.description()); result.setExtensions(extensionsModel(apiOperationAnnotation.extensions())); result.setResponses(apiResponsesModel(openAPI, apiOperationAnnotation.responses())); result.setOperationId(apiOperationAnnotation.operationId()); result.setTags(tagsModel(apiOperationAnnotation.tags())); result.setRequestBody(requestBodyModel(openAPI, apiOperationAnnotation.requestBody())); return result; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.util.CollectionUtils; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; public class ApiResponseMethodProcessor implements MethodAnnotationProcessor { @Override public Type getProcessType() { return ApiResponse.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ApiResponse apiResponse) { List produces = new ArrayList<>(); String responseCode = apiResponse.responseCode(); if (StringUtils.isEmpty(responseCode) || "default".equals(responseCode)) { responseCode = SwaggerConst.SUCCESS_KEY; } Class type = null; for (Content content : apiResponse.content()) { if (StringUtils.isNotEmpty(content.mediaType())) { produces.add(content.mediaType()); } if (content.schema() != null && content.schema().implementation() != Void.class) { type = content.schema().implementation(); } } operationGenerator.getOperationGeneratorContext().updateResponse(responseCode, type == null ? null : SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), type)); if (StringUtils.isNotEmpty(apiResponse.description())) { operationGenerator.getOperationGeneratorContext().updateResponseDescription(responseCode, apiResponse.description()); } for (Header header : apiResponse.headers()) { if (header.schema() == null || header.schema().implementation() == Void.class) { throw new IllegalArgumentException("@ApiResponse header schema implementation must be defined."); } if (StringUtils.isEmpty(header.name())) { throw new IllegalArgumentException("@ApiResponse header name must be defined."); } operationGenerator.getOperationGeneratorContext().updateResponseHeader(responseCode, header.name(), AnnotationUtils.schemaModel(swaggerGenerator.getOpenAPI(), header.schema())); } if (!CollectionUtils.isEmpty(produces)) { operationGenerator.getOperationGeneratorContext().updateProduces(produces); } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.util.CollectionUtils; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; public class ApiResponsesMethodProcessor implements MethodAnnotationProcessor { @Override public Type getProcessType() { return ApiResponses.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ApiResponses apiResponses) { List produces = new ArrayList<>(); for (ApiResponse apiResponse : apiResponses.value()) { if (StringUtils.isEmpty(apiResponse.responseCode()) || "default".equals(apiResponse.responseCode())) { throw new IllegalArgumentException("@ApiResponse status code must be defined."); } Class type = null; for (Content content : apiResponse.content()) { if (StringUtils.isNotEmpty(content.mediaType())) { produces.add(content.mediaType()); } if (content.schema() != null && content.schema().implementation() != Void.class) { type = content.schema().implementation(); } } operationGenerator.getOperationGeneratorContext().updateResponse(apiResponse.responseCode(), type == null ? null : SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), type)); if (StringUtils.isNotEmpty(apiResponse.description())) { operationGenerator.getOperationGeneratorContext().updateResponseDescription(apiResponse.responseCode(), apiResponse.description()); } for (Header header : apiResponse.headers()) { if (header.schema() == null || header.schema().implementation() == Void.class) { throw new IllegalArgumentException("@ApiResponse header schema implementation must be defined."); } if (StringUtils.isEmpty(header.name())) { throw new IllegalArgumentException("@ApiResponse header name must be defined."); } operationGenerator.getOperationGeneratorContext().updateResponseHeader(apiResponse.responseCode(), header.name(), AnnotationUtils.schemaModel(swaggerGenerator.getOpenAPI(), header.schema())); } } if (!CollectionUtils.isEmpty(produces)) { operationGenerator.getOperationGeneratorContext().updateProduces(produces); } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.models.OpenAPI; public class OpenAPIDefinitionProcessor implements ClassAnnotationProcessor { @Override public Type getProcessType() { return OpenAPIDefinition.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OpenAPIDefinition definitionAnnotation) { OpenAPI swagger = swaggerGenerator.getOpenAPI(); swagger.setServers(AnnotationUtils.serversModel(definitionAnnotation.servers())); swagger.setTags(AnnotationUtils.tagsModel(definitionAnnotation.tags())); swagger.setInfo(AnnotationUtils.infoModel(definitionAnnotation.info())); swagger.setExternalDocs(AnnotationUtils.externalDocumentationModel(definitionAnnotation.externalDocs())); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.util.CollectionUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; public class OperationMethodAnnotationProcessor implements MethodAnnotationProcessor { public Type getProcessType() { return Operation.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Operation apiOperationAnnotation) { io.swagger.v3.oas.models.Operation operation = operationGenerator.getOperation(); if (!StringUtils.isEmpty(apiOperationAnnotation.method())) { operationGenerator.setHttpMethod(apiOperationAnnotation.method()); } if (apiOperationAnnotation.responses() != null) { // This is a bit different from swagger annotation intention. // Here we assume @Content defines all supported media type and only one implementation class for one status code. List produces = new ArrayList<>(); for (ApiResponse apiResponse : apiOperationAnnotation.responses()) { if (StringUtils.isEmpty(apiResponse.responseCode()) || "default".equals(apiResponse.responseCode())) { throw new IllegalArgumentException("@ApiResponse status code must be defined."); } Class type = null; for (Content content : apiResponse.content()) { if (StringUtils.isNotEmpty(content.mediaType())) { produces.add(content.mediaType()); } if (content.schema() != null && content.schema().implementation() != Void.class) { type = content.schema().implementation(); } } operationGenerator.getOperationGeneratorContext().updateResponse(apiResponse.responseCode(), type == null ? null : SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), type)); if (StringUtils.isNotEmpty(apiResponse.description())) { operationGenerator.getOperationGeneratorContext().updateResponseDescription(apiResponse.responseCode(), apiResponse.description()); } for (Header header : apiResponse.headers()) { if (header.schema() == null || header.schema().implementation() == Void.class) { throw new IllegalArgumentException("@ApiResponse header schema implementation must be defined."); } if (StringUtils.isEmpty(header.name())) { throw new IllegalArgumentException("@ApiResponse header name must be defined."); } operationGenerator.getOperationGeneratorContext().updateResponseHeader(apiResponse.responseCode(), header.name(), AnnotationUtils.schemaModel(swaggerGenerator.getOpenAPI(), header.schema())); } } if (!CollectionUtils.isEmpty(produces)) { operationGenerator.getOperationGeneratorContext().updateProduces(produces); } } io.swagger.v3.oas.models.Operation specificOperation = AnnotationUtils.operationModel(swaggerGenerator.getOpenAPI(), apiOperationAnnotation); if (!StringUtils.isEmpty(specificOperation.getSummary())) { operation.setSummary(specificOperation.getSummary()); } if (!StringUtils.isEmpty(specificOperation.getDescription())) { operation.setDescription(specificOperation.getDescription()); } if (!StringUtils.isEmpty(specificOperation.getOperationId())) { operation.setOperationId(specificOperation.getOperationId()); } if (!CollectionUtils.isEmpty(specificOperation.getExtensions())) { operation.setExtensions(specificOperation.getExtensions()); } if (!CollectionUtils.isEmpty(specificOperation.getTags())) { operation.setTags(specificOperation.getTags()); } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/RequestBodyMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.oas.annotations.parameters.RequestBody; public class RequestBodyMethodAnnotationProcessor implements MethodAnnotationProcessor { @Override public Type getProcessType() { return RequestBody.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, RequestBody annotation) { io.swagger.v3.oas.models.parameters.RequestBody requestBody = AnnotationUtils.requestBodyModel(swaggerGenerator.getOpenAPI(), annotation); operationGenerator.getOperation().setRequestBody(requestBody); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * swagger竟然没提供从annotation生成swagger对象的功能,只好自己做了 */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/HttpServletRequestContextRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import java.lang.reflect.Type; import jakarta.servlet.http.HttpServletRequest; import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; public class HttpServletRequestContextRegister implements SwaggerContextRegister { @Override public Type getContextType() { return HttpServletRequest.class; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ParameterParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.SwaggerParameterAnnotationProcessor; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; public class ParameterParameterAnnotationProcessor extends SwaggerParameterAnnotationProcessor { @Override public Class getProcessType() { return io.swagger.v3.oas.annotations.Parameter.class; } @Override public String getParameterName(Parameter annotation) { if (StringUtils.isNotEmpty(annotation.name())) { return annotation.name(); } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, io.swagger.v3.oas.annotations.Parameter annotation) { if (annotation.schema() != null && annotation.schema().implementation() != null && annotation.schema().implementation() != Void.class) { parameterGenerator.getParameterGeneratorContext() .setParameterType(TypeFactory.defaultInstance().constructType(annotation.schema().implementation())); } if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } if (annotation.in() != null && annotation.in() != ParameterIn.DEFAULT) { parameterGenerator.setHttpParameterType(HttpParameterType.from(annotation.in())); } if (Explode.TRUE.equals(annotation.explode())) { parameterGenerator.getParameterGeneratorContext().setExplode(true); } if (Explode.FALSE.equals(annotation.explode())) { parameterGenerator.getParameterGeneratorContext().setExplode(false); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartArrayParameterTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.FileSchema; import jakarta.servlet.http.Part; public class PartArrayParameterTypeProcessor extends PartParameterTypeProcessor { @Override public JavaType getProcessType() { return TypeFactory.defaultInstance().constructType(Part[].class); } @Override protected void formParameterSchema(ParameterGenerator parameterGenerator) { parameterGenerator.getParameterGeneratorContext().setSchema(new ArraySchema().items(new FileSchema())); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import java.util.List; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import jakarta.servlet.http.Part; public class PartListParameterTypeProcessor extends PartArrayParameterTypeProcessor { @Override public JavaType getProcessType() { return TypeFactory.defaultInstance().constructType(ParameterizedTypeUtil.make(List.class, Part.class)); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartParameterTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.ParameterTypeProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.media.FileSchema; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.MediaType; public class PartParameterTypeProcessor implements ParameterTypeProcessor { @Override public JavaType getProcessType() { return TypeFactory.defaultInstance().constructType(Part.class); } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator) { if (!parameterGenerator.getParameterGeneratorContext().getSupportedConsumes() .contains(MediaType.MULTIPART_FORM_DATA)) { throw new IllegalArgumentException("Part type must declare consumes " + MediaType.MULTIPART_FORM_DATA); } if (parameterGenerator.getHttpParameterType() != HttpParameterType.FORM) { throw new IllegalArgumentException( getProcessType().getRawClass().getSimpleName() + " type must declare as form parameter."); } if (StringUtils.isEmpty(parameterGenerator.getParameterGeneratorContext().getParameterName())) { throw new IllegalArgumentException( "Name is required for parameter " + getProcessType().getRawClass().getSimpleName()); } parameterGenerator.getParameterGeneratorContext().updateConsumes(List.of(MediaType.MULTIPART_FORM_DATA)); formParameterSchema(parameterGenerator); } protected void formParameterSchema(ParameterGenerator parameterGenerator) { parameterGenerator.getParameterGeneratorContext().setSchema(new FileSchema()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RawJsonRequestBodyProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.SwaggerParameterAnnotationProcessor; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import jakarta.ws.rs.core.MediaType; public class RawJsonRequestBodyProcessor extends SwaggerParameterAnnotationProcessor { @Override public Class getProcessType() { return RawJsonRequestBody.class; } @Override public String getParameterName(RawJsonRequestBody annotation) { if (StringUtils.isNotEmpty(annotation.value())) { return annotation.value(); } else if (StringUtils.isNotEmpty(annotation.name())) { return annotation.name(); } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, RawJsonRequestBody annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.BODY); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); parameterGenerator.getParameterGeneratorContext().setRawJson(true); parameterGenerator.getParameterGeneratorContext().updateConsumes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RequestBodyParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.SwaggerParameterAnnotationProcessor; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import io.swagger.v3.oas.annotations.parameters.RequestBody; public class RequestBodyParameterAnnotationProcessor extends SwaggerParameterAnnotationProcessor { @Override public Class getProcessType() { return io.swagger.v3.oas.annotations.parameters.RequestBody.class; } @Override public String getParameterName(RequestBody requestBody) { return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, io.swagger.v3.oas.annotations.parameters.RequestBody annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.BODY); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.parameter; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; import io.vertx.core.http.ServerWebSocket; public class ServerWebSocketContextRegister implements SwaggerContextRegister { @Override public Type getContextType() { return ServerWebSocket.class; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/CompletableFutureProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.response; import java.util.concurrent.CompletableFuture; public class CompletableFutureProcessor extends DefaultResponseTypeProcessor { public CompletableFutureProcessor() { extractActualType = true; } @Override public Class getProcessType() { return CompletableFuture.class; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.response; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findResponseTypeProcessor; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ResponseTypeProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import io.swagger.v3.core.util.ReflectionUtils; import io.swagger.v3.oas.models.media.Schema; import jakarta.servlet.http.Part; public class DefaultResponseTypeProcessor implements ResponseTypeProcessor { protected boolean extractActualType; @Override public Type getProcessType() { // not care for this. return null; } @Override public Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType) { if (extractActualType) { genericResponseType = ((ParameterizedType) genericResponseType).getActualTypeArguments()[0]; } return doExtractResponseType(genericResponseType); } private Type doExtractResponseType(Type genericResponseType) { if (!(genericResponseType instanceof ParameterizedType)) { return genericResponseType; } // eg: // genericResponseType is CompletableFuture> // responseType is ResponseEntity // responseRawType is ResponseEntity Type responseRawType = genericResponseType; if (genericResponseType instanceof ParameterizedType) { responseRawType = ((ParameterizedType) genericResponseType).getRawType(); } ResponseTypeProcessor processor = findResponseTypeProcessor(responseRawType); if (responseRawType.equals(processor.getProcessType())) { return processor.extractResponseType(genericResponseType); } return genericResponseType; } @Override public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType) { Type responseType = extractResponseType(swaggerGenerator, operationGenerator, genericResponseType); if (responseType == null || ReflectionUtils.isVoid(responseType)) { return null; } if (responseType instanceof Class && Part.class.isAssignableFrom((Class) responseType)) { responseType = Part.class; } return SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), responseType); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/OptionalProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.response; import java.lang.reflect.Type; import java.util.Optional; public class OptionalProcessor extends DefaultResponseTypeProcessor { public OptionalProcessor() { extractActualType = true; } @Override public Type getProcessType() { return Optional.class; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.response; import java.lang.reflect.Type; import java.util.List; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.reactivestreams.Publisher; import io.swagger.v3.oas.models.media.Schema; import jakarta.ws.rs.core.MediaType; public class PublisherProcessor extends DefaultResponseTypeProcessor { public PublisherProcessor() { extractActualType = true; } @Override public Type getProcessType() { return Publisher.class; } @Override public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType) { operationGenerator.getOperationGeneratorContext() .updateProduces(List.of(MediaType.SERVER_SENT_EVENTS)); return super.process(swaggerGenerator, operationGenerator, genericResponseType); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.unittest; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; public final class UnitTestSwaggerUtils { private static final ObjectWriter writer = Yaml.pretty(); private UnitTestSwaggerUtils() { } public static String loadExpect(String resPath) { URL url = Thread.currentThread().getContextClassLoader().getResource(resPath); if (url == null) { return "can not found res " + resPath; } try { return IOUtils.toString(url, StandardCharsets.UTF_8); } catch (IOException e) { return e.getMessage(); } } public static String pretty(OpenAPI swagger) { try { return writer.writeValueAsString(swagger); } catch (JsonProcessingException e) { throw new Error(e); } } public static OpenAPI parse(String content) { try { return Yaml.mapper().readValue(content, OpenAPI.class); } catch (Exception e) { return new OpenAPI(); // throw new Error(e); } } public static SwaggerGenerator testSwagger(String resPath, Class cls, String... methods) { SwaggerGenerator generator = SwaggerGenerator.create(cls); generator.replaceMethodWhiteList(methods); OpenAPI swagger = generator.generate(); String schema = pretty(swagger).trim(); String expectSchema = loadExpect(resPath).replace("\r\n", "\n").trim(); int offset = expectSchema.indexOf("---\nopenapi: 3.0.1"); if (offset > 0) { expectSchema = expectSchema.substring(offset + 4); } try { ObjectMapper yaml = new ObjectMapper(new YAMLFactory()); JsonNode expected = yaml.readTree(expectSchema); JsonNode actual = yaml.readTree(schema); if (!actual.equals(expected)) { ObjectMapper json = new ObjectMapper(); String expectedPretty = json.writerWithDefaultPrettyPrinter().writeValueAsString(expected); String actualPretty = json.writerWithDefaultPrettyPrinter().writeValueAsString(actual); Assertions.fail("OpenAPI mismatch.\n=== EXPECTED ===\n" + expectedPretty + "\n=== ACTUAL ===\n" + actualPretty); } } catch (Exception e) { Assertions.fail("Failed to parse/compare OpenAPI YAML: " + e.getMessage(), e); } return generator; } public static Throwable getException(Class cls, String... methods) { try { SwaggerGenerator generator = SwaggerGenerator.create(cls); generator.replaceMethodWhiteList(methods); generator.generate(); } catch (Throwable e) { return e; } // 不允许成功 Assertions.assertEquals("not allowed run to here", "run to here"); return null; } public static void testException(String expectMsgLevel1, String expectMsgLevel2, String expectMsgLevel3, Class cls, String... methods) { Throwable exception = getException(cls, methods); Assertions.assertEquals(expectMsgLevel1, exception.getMessage()); Assertions.assertEquals(expectMsgLevel2, exception.getCause().getMessage()); Assertions.assertEquals(expectMsgLevel3, exception.getCause().getCause().getMessage()); } public static void testException(String expectMsgLevel1, String expectMsgLevel2, Class cls, String... methods) { Throwable exception = getException(cls, methods); Assertions.assertEquals(expectMsgLevel1, exception.getMessage()); Assertions.assertEquals(expectMsgLevel2, exception.getCause().getMessage()); } public static void testException(String expectMsg, Class cls, String... methods) { Throwable exception = getException(cls, methods); Assertions.assertEquals(expectMsg, exception.getMessage()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/package-info.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 专用于ut场景 */ package org.apache.servicecomb.swagger.generator.core.unittest; ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import io.swagger.v3.oas.annotations.Operation; public class MethodUtils { /** * Get the methods of cls which are valid for generating Swagger schema. * @param cls The REST interface class, or so called "controller" class, to be analysed. * @return the valid methods to be used to generate Swagger schema, sorted by their Swagger operation name. */ public static List findSwaggerMethods(Class cls) { Method[] methods = cls.getMethods(); List result = new ArrayList<>(methods.length); for (Method m : methods) { if (!isSkipMethod(cls, m)) { result.add(m); } } // order of cls.getMethods() is undefined and not stable // so we must sort them first to make generation is stable result.sort(Comparator.comparing(MethodUtils::findSwaggerMethodName)); return result; } public static Map findSwaggerMethodsMapOfOperationId(Class cls) { List methods = findSwaggerMethods(cls); Map result = new HashMap<>(); methods.forEach((item) -> result.put(findSwaggerMethodName(item), item)); return result; } /** * Pick out those methods not proper to be added into the Swagger schema. * * @param cls the owner class of the method * @param method the method to be validate * @return true if this method should be abandoned; * false if this method should be added in to Swagger schema */ public static boolean isSkipMethod(Class cls, Method method) { if (method.isDefault()) { return true; } if (method.getDeclaringClass() == Object.class) { return true; } if (method.getDeclaringClass().isInterface() && !cls.isInterface()) { // inherited template methods return true; } // skip static method int modifiers = method.getModifiers(); if (Modifier.isStatic(modifiers)) { return true; } // skip bridge method if (method.isBridge()) { return true; } Operation apiOperation = method.getAnnotation(Operation.class); return apiOperation != null && apiOperation.hidden(); } /** * Get the operationId in schema of this method, * no matter whether it should be hidden. * @return If the operation name is specified via {@link Operation}, use that one. * Otherwise the method name is returned. */ public static String findSwaggerMethodName(Method method) { Operation apiOperationAnnotation = method.getAnnotation(Operation.class); if (apiOperationAnnotation == null || StringUtils.isEmpty(apiOperationAnnotation.operationId())) { return method.getName(); } return apiOperationAnnotation.operationId(); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoOperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.pojo; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.isContextParameter; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.core.AbstractOperationGenerator; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; import com.fasterxml.jackson.databind.type.TypeFactory; import com.google.common.reflect.TypeToken; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import jakarta.ws.rs.HttpMethod; public class PojoOperationGenerator extends AbstractOperationGenerator { public PojoOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { super(swaggerGenerator, method); } @Override protected void initParameterGenerators() { List bodyParameters = new ArrayList<>(); for (java.lang.reflect.Parameter methodParameter : method.getParameters()) { Type type = TypeToken.of(clazz) .resolveType(methodParameter.getParameterizedType()) .getType(); ParameterGenerator parameterGenerator = new ParameterGenerator( this, Collections.emptyMap(), methodParameter, TypeFactory.defaultInstance().constructType(type)); validateParameter(parameterGenerator.getGenericType()); if (isContextParameter(parameterGenerator.getGenericType())) { continue; } bodyParameters.add(parameterGenerator); } tryWrapParametersToBody(bodyParameters); } private void tryWrapParametersToBody(List bodyParameters) { if (bodyParameters.size() == 0) { return; } if (bodyParameters.size() == 1 && SwaggerUtils.isBean(bodyParameters.get(0).getGenericType())) { ParameterGenerator parameterGenerator = bodyParameters.get(0); parameterGenerator.setHttpParameterType(HttpParameterType.BODY); parameterGenerators.add(parameterGenerator); return; } wrapParametersToBody(bodyParameters); } private void wrapParametersToBody(List bodyFields) { // process annotations like parameter name for (ParameterGenerator parameterGenerator : bodyFields) { scanMethodParameter(parameterGenerator); } String simpleRef = MethodUtils.findSwaggerMethodName(method) + "Body"; Schema bodyModel = new ObjectSchema(); for (ParameterGenerator parameterGenerator : bodyFields) { bodyModel.addProperty(parameterGenerator.getParameterGeneratorContext().getParameterName(), parameterGenerator.getParameterGeneratorContext().getSchema()); } swagger.getComponents().addSchemas(simpleRef, bodyModel); Schema bodyModelNew = new Schema<>(); bodyModelNew.set$ref(Components.COMPONENTS_SCHEMAS_REF + simpleRef); ParameterGenerator newParameterGenerator = new ParameterGenerator(this, simpleRef, bodyModelNew); newParameterGenerator.setHttpParameterType(HttpParameterType.BODY); parameterGenerators.add(newParameterGenerator); } @Override public void correctOperation() { correctPath(); correctHttpMethod(); super.correctOperation(); } protected void correctPath() { if (StringUtils.isEmpty(path)) { path = "/" + getOperationId(); } } protected void correctHttpMethod() { if (StringUtils.isEmpty(httpMethod)) { setHttpMethod(HttpMethod.POST); } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.pojo; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; import jakarta.ws.rs.core.MediaType; public class PojoSwaggerGenerator extends AbstractSwaggerGenerator { protected static final List SUPPORTED_CONTENT_TYPE = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN); public PojoSwaggerGenerator(Class cls) { super(cls); swaggerGeneratorContext.updateConsumes(SUPPORTED_CONTENT_TYPE); swaggerGeneratorContext.updateProduces(SUPPORTED_CONTENT_TYPE); } @SuppressWarnings("unchecked") @Override public T createOperationGenerator(Method method) { return (T) new PojoOperationGenerator(this, method); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.pojo; import java.lang.annotation.Annotation; import jakarta.ws.rs.Path; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PojoSwaggerGeneratorFactory implements SwaggerGeneratorFactory { private static final Logger LOGGER = LoggerFactory.getLogger(PojoSwaggerGeneratorFactory.class); @Override public int getOrder() { return Integer.MAX_VALUE; } @Override public boolean canProcess(Class cls) { for (Annotation annotation : cls.getAnnotations()) { // we check the annotations by class name to avoid importing extra dependencies in this module if (annotation instanceof Path || "org.springframework.web.bind.annotation.RequestMapping" .equals(annotation.annotationType().getCanonicalName())) { LOGGER.info( "There is @RequestMapping or @Path annotation on the REST interface class, but POJO swagger generator is chosen. " + "If this is unexpected, maybe you should check your dependency jar files."); } } return true; } @Override public SwaggerGenerator create(Class cls) { return new PojoSwaggerGenerator(cls); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestOperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.rest; import java.lang.reflect.Method; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.core.AbstractOperationGenerator; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; public abstract class RestOperationGenerator extends AbstractOperationGenerator { public RestOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { super(swaggerGenerator, method); } @Override public void correctOperation() { checkPath(); correctPath(); super.correctOperation(); } protected void checkPath() { if (StringUtils.isEmpty(path) && StringUtils.isEmpty(SwaggerUtils.getBasePath(swagger))) { throw new IllegalStateException("Path must not both be empty in class and method"); } } protected void correctPath() { if (StringUtils.isEmpty(path)) { path = "/"; } } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestSwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.rest; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; public abstract class RestSwaggerGenerator extends AbstractSwaggerGenerator { public RestSwaggerGenerator(Class cls) { super(cls); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.v3.core.converter.ModelConverter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.extend.ModelResolverExt ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.core.processor.annotation.OpenAPIDefinitionProcessor ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.core.processor.annotation.OperationMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.core.processor.annotation.ApiResponsesMethodProcessor org.apache.servicecomb.swagger.generator.core.processor.annotation.ApiResponseMethodProcessor org.apache.servicecomb.swagger.generator.core.processor.annotation.RequestBodyMethodAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.core.processor.parameter.RequestBodyParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.ParameterParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.RawJsonRequestBodyProcessor ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.core.processor.parameter.PartParameterTypeProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.PartListParameterTypeProcessor ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.core.processor.response.CompletableFutureProcessor org.apache.servicecomb.swagger.generator.core.processor.response.OptionalProcessor org.apache.servicecomb.swagger.generator.core.processor.response.PublisherProcessor ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.core.processor.parameter.HttpServletRequestContextRegister org.apache.servicecomb.swagger.generator.core.processor.parameter.ServerWebSocketContextRegister ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator ================================================ FILE: swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorFactory ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/TestSwaggerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; public class TestSwaggerUtils { @Test public void swaggerToStringNormal() { OpenAPI swagger = new OpenAPI(); String content = SwaggerUtils.swaggerToString(swagger); OpenAPI newSwagger = SwaggerUtils.parseSwagger(content); Assertions.assertEquals(swagger, newSwagger); } @Test public void parseSwaggerUrlNormal() throws IOException { String content = "openapi: 3.0.1"; URL url = Mockito.mock(URL.class); try (MockedStatic ioUtilsMockedStatic = Mockito.mockStatic(IOUtils.class)) { ioUtilsMockedStatic.when(() -> IOUtils.toString(url, StandardCharsets.UTF_8)).thenReturn(content); OpenAPI swagger = Yaml.mapper().readValue(content, OpenAPI.class); OpenAPI result = SwaggerUtils.parseAndValidateSwagger(url); Assertions.assertEquals(swagger, result); Assertions.assertEquals("3.0.1", result.getOpenapi()); } } @Test public void parseSwaggerUrlException() throws IOException { URL url = Mockito.mock(URL.class); try (MockedStatic ioUtilsMockedStatic = Mockito.mockStatic(IOUtils.class)) { ioUtilsMockedStatic.when(() -> IOUtils.toString(url, StandardCharsets.UTF_8)) .thenThrow(new RuntimeExceptionWithoutStackTrace("failed")); ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, () -> SwaggerUtils.parseAndValidateSwagger(url)); Assertions.assertTrue(exception.getMessage().contains("Parse swagger from url failed, ")); } } @Test public void parseSwaggerContentException() { ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, () -> SwaggerUtils.parseSwagger("")); Assertions.assertEquals("Parse swagger from content failed, ", exception.getMessage()); } @Test public void correctResponsesOperationFixEmptyDescription() { ApiResponse response = new ApiResponse(); Operation operation = new Operation(); operation.setResponses(new ApiResponses()); operation.getResponses().addApiResponse("200", response); SwaggerUtils.correctResponses(operation); Assertions.assertEquals("response of 200", response.getDescription()); } @Test public void correctResponsesOperationNotChangeExistDescription() { ApiResponse response = new ApiResponse(); response.setDescription("description"); Operation operation = new Operation(); operation.setResponses(new ApiResponses()); operation.getResponses().addApiResponse("200", response); SwaggerUtils.correctResponses(operation); Assertions.assertEquals("description", response.getDescription()); } @Test public void correctResponsesHavePaths() { ApiResponse response = new ApiResponse(); Operation operation = new Operation(); operation.setResponses(new ApiResponses()); operation.getResponses().addApiResponse("200", response); PathItem path = new PathItem(); path.get(operation); OpenAPI swagger = new OpenAPI(); swagger.path("/base", path); SwaggerUtils.correctResponses(swagger); Assertions.assertEquals("response of 200", response.getDescription()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospectorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.introspector; import com.fasterxml.jackson.annotation.JsonProperty; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class JsonPropertyIntrospectorTest { @Test public void findEnumValue() { JsonPropertyIntrospector introspector = new JsonPropertyIntrospector(); Assertions.assertEquals("AB", introspector.findEnumValue(TestEnum.AB)); Assertions.assertEquals("C-D", introspector.findEnumValue(TestEnum.C_D)); Assertions.assertEquals("E.F", introspector.findEnumValue(TestEnum.E_F)); Assertions.assertEquals("HI", introspector.findEnumValue(TestEnum.HI)); } public enum TestEnum { AB, @JsonProperty(value = "C-D") C_D, @JsonProperty(value = "E.F") E_F, @JsonProperty HI } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.module; import static org.hamcrest.Matchers.contains; import java.util.List; import org.apache.servicecomb.swagger.SwaggerUtils; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; @SuppressWarnings({"rawtypes", "unchecked"}) public class EnumModuleExtTest { public enum TestEnum { AB, @JsonProperty(value = "C-D") C_D, @JsonProperty(value = "E.F") E_F, @JsonProperty HI } @Test public void testEnumModule() throws JsonProcessingException { ObjectMapper mapper = Json.mapper(); String serializeValue = mapper.writeValueAsString(TestEnum.AB); Assertions.assertEquals("\"AB\"", serializeValue); serializeValue = mapper.writeValueAsString(TestEnum.C_D); Assertions.assertEquals("\"C-D\"", serializeValue); serializeValue = mapper.writeValueAsString(TestEnum.E_F); Assertions.assertEquals("\"E.F\"", serializeValue); serializeValue = mapper.writeValueAsString(TestEnum.HI); Assertions.assertEquals("\"\"", serializeValue); } @Test public void testEnumModuleModel() { OpenAPI openAPI = new OpenAPI(); Schema schema = SwaggerUtils.resolveTypeSchemas(openAPI, TestEnum.class); Assertions.assertEquals(schema.getType(), "string"); MatcherAssert.assertThat((List) schema.getEnum(), contains("AB", "C-D", "E.F", "")); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/property/creator/TestPartPropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.extend.property.creator; import jakarta.servlet.http.Part; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.media.FileSchema; public class TestPartPropertyCreator { PartPropertyCreator creator = new PartPropertyCreator(); @SuppressWarnings("unchecked") @Test public void classes() { MatcherAssert.assertThat(creator.classes(), Matchers.arrayContaining(Part.class)); } @Test public void createProperty() { MatcherAssert.assertThat(creator.createProperty(), Matchers.instanceOf(FileSchema.class)); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.extensions.Extension; import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; @SuppressWarnings({"rawtypes", "unused"}) public class TestApiOperation { static SwaggerOperations swaggerOperations = SwaggerOperations.generate(ApiOperationAnnotation.class); @AfterAll public static void teardown() { swaggerOperations = null; } interface ApiOperationAnnotation { @Operation( summary = "summary", description = "notes", tags = {"tag1", "tag2"}, method = "GET", operationId = "test", responses = @ApiResponse(responseCode = "202", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = String.class)), headers = @Header(name = "h1", schema = @Schema(implementation = Integer.class))), extensions = {@Extension( name = "x-tagA", properties = {@ExtensionProperty(name = "x-tagAExt", value = "value of tagAExt")})}) void testBase(); @Operation(summary = "aaa") @ApiResponse(responseCode = "202", content = @Content(schema = @Schema(implementation = String.class))) int testPrimitive(); @Operation(summary = "aaa", hidden = true) int testHidden(); } @Test public void testApiOperation() { OpenAPI swagger = swaggerOperations.getSwagger(); testBase(swagger.getPaths().get("/test")); testPrimitive(swagger.getPaths().get("/testPrimitive")); MatcherAssert.assertThat(swagger.getPaths().get("/testHidden"), is(nullValue())); } private void testPrimitive(PathItem path) { io.swagger.v3.oas.models.Operation operation = path.getPost(); Assertions.assertEquals(2, operation.getResponses().size()); io.swagger.v3.oas.models.media.Schema result200 = operation.getResponses().get("200").getContent().get(MediaType.APPLICATION_JSON).getSchema(); Assertions.assertEquals("integer", result200.getType()); Assertions.assertEquals("int32", result200.getFormat()); io.swagger.v3.oas.models.media.Schema result202 = operation.getResponses().get("202").getContent().get(MediaType.APPLICATION_JSON).getSchema(); Assertions.assertEquals("string", result202.getType()); } private void testBase(PathItem path) { Assertions.assertEquals(1, path.readOperations().size()); io.swagger.v3.oas.models.Operation operation = path.getGet(); Assertions.assertEquals("summary", operation.getSummary()); Assertions.assertEquals("notes", operation.getDescription()); Assertions.assertEquals(Arrays.asList("tag1", "tag2"), operation.getTags()); ApiResponses responseMap = operation.getResponses(); Assertions.assertEquals(2, responseMap.size()); io.swagger.v3.oas.models.responses.ApiResponse response = responseMap.get("202"); Assertions.assertNotNull(response); Assertions.assertEquals("string", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); Assertions.assertEquals(1, response.getHeaders().size()); Assertions.assertEquals("integer", response.getHeaders().get("h1").getSchema().getType()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestApiResponse { SwaggerOperations swaggerOperations; @BeforeAll public void setUp() { swaggerOperations = SwaggerOperations.generate(ApiResponseAnnotation.class); } @AfterAll public void tearDown() { swaggerOperations = null; } interface ApiResponseAnnotation { @ApiResponse( headers = {@Header(name = "k1", schema = @Schema(implementation = Integer.class)), @Header(name = "k2", schema = @Schema(implementation = String.class))}, responseCode = "200", description = "") void testApiResponseHeader(); @ApiResponse(responseCode = "200", headers = {@Header(name = "k1", schema = @Schema(implementation = Integer.class))}) void testResponseHeader(); @ApiResponse( content = @Content(schema = @Schema(implementation = Integer.class)), responseCode = "200", description = "msg") void testSingle(); @ApiResponses(value = { @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Integer.class)), description = "msg1"), @ApiResponse(responseCode = "301", content = @Content(schema = @Schema(implementation = String.class)), description = "msg2")}) void testMulti(); } @Test public void checkResponseHeader() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testResponseHeader"); Assertions.assertEquals("/testResponseHeader", swaggerOperation.getPath()); io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); io.swagger.v3.oas.models.headers.Header property = response.getHeaders().get("k1"); Assertions.assertEquals("integer", property.getSchema().getType()); Assertions.assertEquals("int32", property.getSchema().getFormat()); } @Test public void checkResponseDesc() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMulti"); Assertions.assertEquals("/testMulti", swaggerOperation.getPath()); io.swagger.v3.oas.models.responses.ApiResponse response1 = swaggerOperation.getOperation().getResponses() .get("200"); io.swagger.v3.oas.models.responses.ApiResponse response2 = swaggerOperation.getOperation().getResponses() .get("301"); Assertions.assertEquals("msg1", response1.getDescription()); Assertions.assertEquals("msg2", response2.getDescription()); } @Test public void checkApiResponseHeader() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testApiResponseHeader"); Assertions.assertEquals("/testApiResponseHeader", swaggerOperation.getPath()); io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); io.swagger.v3.oas.models.headers.Header property = response.getHeaders().get("k1"); Assertions.assertEquals("integer", property.getSchema().getType()); Assertions.assertEquals("int32", property.getSchema().getFormat()); property = response.getHeaders().get("k2"); Assertions.assertEquals("string", property.getSchema().getType()); Assertions.assertEquals(null, property.getSchema().getFormat()); } @Test public void checkSingle() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testSingle"); Assertions.assertEquals("/testSingle", swaggerOperation.getPath()); io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); Assertions.assertEquals("integer", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); Assertions.assertEquals("int32", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getFormat()); } @Test public void checkMulti() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMulti"); Assertions.assertEquals("/testMulti", swaggerOperation.getPath()); io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); Assertions.assertEquals("integer", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); Assertions.assertEquals("int32", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getFormat()); response = swaggerOperation.getOperation().getResponses().get("301"); Assertions.assertEquals("string", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); Assertions.assertEquals(null, response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getFormat()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.apache.servicecomb.swagger.generator.core.schema.ArrayType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.ByteArraySchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.RequestBody; import jakarta.ws.rs.core.MediaType; public class TestArrayType { @Test public void test() { SwaggerOperations swaggerOperations = SwaggerOperations.generate(ArrayType.class); SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBytes"); RequestBody bodyParameter = swaggerOperation.getOperation().getRequestBody(); Schema model = bodyParameter.getContent().get(MediaType.APPLICATION_JSON).getSchema(); Assertions.assertEquals(Components.COMPONENTS_SCHEMAS_REF + "testBytesBody", model.get$ref()); OpenAPI openAPI = swaggerOperation.getSwagger(); Schema schema = openAPI.getComponents().getSchemas().get("testBytesBody"); Assertions.assertEquals(1, schema.getProperties().size()); ByteArraySchema arrayProperty = (ByteArraySchema) schema.getProperties().get("value"); Assertions.assertEquals("string", arrayProperty.getType()); Assertions.assertEquals("byte", arrayProperty.getFormat()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.models.parameters.RequestBody; import jakarta.ws.rs.Path; @OpenAPIDefinition public class TestClassUtils { @Test public void testHasAnnotation() { Assertions.assertTrue(SwaggerUtils.hasAnnotation(TestClassUtils.class, OpenAPIDefinition.class)); Assertions.assertTrue(SwaggerUtils.hasAnnotation(TestClassUtils.class, Test.class)); Assertions.assertFalse(SwaggerUtils.hasAnnotation(TestClassUtils.class, Path.class)); } @Test public void isRawJsonType() { RequestBody param = new RequestBody(); Assertions.assertFalse(SwaggerUtils.isRawJsonType(param)); param.addExtension(SwaggerConst.EXT_RAW_JSON_TYPE, Boolean.FALSE); Assertions.assertFalse(SwaggerUtils.isRawJsonType(param)); param.addExtension(SwaggerConst.EXT_RAW_JSON_TYPE, Boolean.TRUE); Assertions.assertTrue(SwaggerUtils.isRawJsonType(param)); } @Test public void getClassName_noName() { Assertions.assertNull(SwaggerUtils.getClassName(null)); Map vendorExtensions = new HashMap<>(); Assertions.assertNull(SwaggerUtils.getClassName(vendorExtensions)); } @Test public void getClassName_normal() { Map vendorExtensions = new HashMap<>(); vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, String.class.getName()); Assertions.assertSame(String.class.getName(), SwaggerUtils.getClassName(vendorExtensions)); } @Test public void getInterfaceName_noName() { Map vendorExtensions = new HashMap<>(); Assertions.assertNull(SwaggerUtils.getInterfaceName(vendorExtensions)); } @Test public void getInterfaceName_normal() { Map vendorExtensions = new HashMap<>(); vendorExtensions.put(SwaggerConst.EXT_JAVA_INTF, String.class.getName()); Assertions.assertSame(String.class.getName(), SwaggerUtils.getInterfaceName(vendorExtensions)); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import java.util.List; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.extensions.Extension; import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestOperationGenerator { SwaggerOperations swaggerOperations; @BeforeAll public void setUp() { swaggerOperations = SwaggerOperations.generate(TestClass.class); } @AfterAll public void tearDown() { swaggerOperations = null; } @OpenAPIDefinition(tags = {@Tag(name = "default0"), @Tag(name = "default1")}) private static class TestClass { @ApiResponse(responseCode = "200", description = "200 is ok............", content = @Content(mediaType = "application/json", schema = @Schema(name = "String")), headers = @Header(name = "x-user-domain", schema = @Schema(implementation = String.class))) @Operation(summary = "value1", tags = {"tag1", "tag2"}, responses = { @ApiResponse(responseCode = "200", headers = {@Header(name = "x-user-name", schema = @Schema(implementation = String.class)), @Header(name = "x-user-id", schema = @Schema(implementation = String.class))})}, extensions = { @Extension(name = "x-class-name", properties = @ExtensionProperty(value = "value", name = "key"))}) public void responseThenApiOperation() { } @Operation(summary = "value1", tags = {"tag1", "tag2"}, responses = {@ApiResponse(responseCode = "200", headers = { @Header(name = "x-user-name", schema = @Schema(implementation = String.class)), @Header(name = "x-user-id", schema = @Schema(implementation = String.class))})}, extensions = { @Extension(name = "x-class-name", properties = { @ExtensionProperty(value = "value", name = "key")})}) @ApiResponse(responseCode = "200", description = "200 is ok............", content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class)), headers = @Header(name = "x-user-domain", schema = @Schema(implementation = String.class))) public void apiOperationThenResponse() { } @Operation(summary = "value2") public void apiOperationNoTag() { } public void noApiOperation() { } } @Test public void apiOperationNoTag() { SwaggerOperation operation = swaggerOperations.findOperation("apiOperationNoTag"); List tags = operation.getOperation().getTags(); MatcherAssert.assertThat(tags, equalTo(null)); Assertions.assertEquals("value2", operation.getOperation().getSummary()); } @Test public void noApiOperation() { SwaggerOperation operation = swaggerOperations.findOperation("noApiOperation"); List tags = operation.getOperation().getTags(); MatcherAssert.assertThat(tags, equalTo(null)); Assertions.assertNull(operation.getOperation().getSummary()); } @Test public void responseThenApiOperation() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("responseThenApiOperation"); List tags = swaggerOperation.getOperation().getTags(); MatcherAssert.assertThat(tags, contains("tag1", "tag2")); io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); Assertions.assertEquals("200 is ok............", response.getDescription()); Assertions.assertNotNull(response.getHeaders().get("x-user-domain")); Assertions.assertNotNull(response.getHeaders().get("x-user-name")); Assertions.assertNotNull(swaggerOperation.getOperation().getExtensions().get("x-class-name")); Assertions.assertNull(swaggerOperation.getOperation().getExtensions().get("x-not-exists")); } @Test public void apiOperationThenResponse() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("apiOperationThenResponse"); List tags = swaggerOperation.getOperation().getTags(); MatcherAssert.assertThat(tags, contains("tag1", "tag2")); io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); Assertions.assertEquals("200 is ok............", response.getDescription()); Assertions.assertNotNull(response.getHeaders().get("x-user-domain")); Assertions.assertNotNull(response.getHeaders().get("x-user-name")); Assertions.assertNotNull(swaggerOperation.getOperation().getExtensions().get("x-class-name")); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; public class TestPojo { @SuppressWarnings("unused") public static class ParameterAnnotation { @Operation(summary = "differentName", operationId = "differentName") public int operationIdAndParameter(@Parameter(name = "x") int a, @Parameter(name = "y") int b) { return a * 2 + b; } } @Test public void testResponseEntity() { UnitTestSwaggerUtils.testSwagger("schemas/ParameterAnnotation.yaml", ParameterAnnotation.class); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojoExample.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import org.apache.servicecomb.swagger.generator.core.pojo.PojoExample1; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.junit.jupiter.api.Test; public class TestPojoExample { @Test public void testPojoExample1() { UnitTestSwaggerUtils.testSwagger("schemas/pojoExample1.yaml", PojoExample1.class); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerDefinition.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import java.util.Map; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.extensions.Extension; import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.info.Contact; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestSwaggerDefinition { @OpenAPIDefinition( servers = @Server(url = "host/base"), tags = {@Tag( name = "tagA", description = "desc of tagA", externalDocs = @ExternalDocumentation(description = "tagA ext docs", url = "url of tagA ext docs"), extensions = {@Extension( name = "x-tagA", properties = {@ExtensionProperty(name = "x-tagAExt", value = "value of tagAExt")})})}, info = @Info( title = "title of SwaggerAnnotation", version = "0.1", termsOfService = "termsOfService", description = "description of info for SwaggerAnnotation", contact = @Contact(name = "contact", email = "contact@email.com", url = "http://contact"), license = @License(name = "license ", url = "http://license"), extensions = {@Extension( name = "x-info", properties = {@ExtensionProperty(name = "x-infoExt", value = "value of infoExt")})}), externalDocs = @ExternalDocumentation( description = "SwaggerAnnotation ext docs", url = "url of SwaggerAnnotation ext docs")) interface SwaggerAnnotation { } interface SwaggerNoAnnotation { } @Test public void testSwaggerDefinition() { OpenAPI swagger = SwaggerGenerator.generate(SwaggerAnnotation.class); Assertions.assertEquals("value of infoExt", ((Map) swagger.getInfo().getExtensions().get("x-info")).get("x-infoExt")); Assertions.assertEquals("3.0.1", swagger.getOpenapi()); Assertions.assertEquals("host/base", swagger.getServers().get(0).getUrl()); Assertions.assertEquals(1, swagger.getTags().size()); io.swagger.v3.oas.models.tags.Tag tagA = swagger.getTags().get(0); Assertions.assertEquals("tagA", tagA.getName()); Assertions.assertEquals("desc of tagA", tagA.getDescription()); Assertions.assertEquals("tagA ext docs", tagA.getExternalDocs().getDescription()); Assertions.assertEquals("url of tagA ext docs", tagA.getExternalDocs().getUrl()); Assertions.assertEquals(1, tagA.getExtensions().size()); Map tagValue = (Map) tagA.getExtensions().get("x-tagA"); Assertions.assertEquals("value of tagAExt", tagValue.get("x-tagAExt")); io.swagger.v3.oas.models.info.Info info = swagger.getInfo(); Assertions.assertEquals("title of SwaggerAnnotation", info.getTitle()); Assertions.assertEquals("0.1", info.getVersion()); Assertions.assertEquals("termsOfService", info.getTermsOfService()); Assertions.assertEquals("description of info for SwaggerAnnotation", info.getDescription()); Assertions.assertEquals("contact", info.getContact().getName()); Assertions.assertEquals("contact@email.com", info.getContact().getEmail()); Assertions.assertEquals("http://contact", info.getContact().getUrl()); Assertions.assertEquals("license ", info.getLicense().getName()); Assertions.assertEquals("http://license", info.getLicense().getUrl()); Assertions.assertEquals("SwaggerAnnotation ext docs", swagger.getExternalDocs().getDescription()); Assertions.assertEquals("url of SwaggerAnnotation ext docs", swagger.getExternalDocs().getUrl()); } @Test public void testFillDefault() { OpenAPI swagger = SwaggerGenerator.generate(SwaggerNoAnnotation.class); Assertions.assertEquals("3.0.1", swagger.getOpenapi()); Assertions.assertEquals("/SwaggerNoAnnotation", swagger.getServers().get(0).getUrl()); Assertions.assertEquals("swagger definition for " + SwaggerNoAnnotation.class.getName(), swagger.getInfo().getTitle()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; public class TestSwaggerGenerator { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { LegacyPropertyFactory.setEnvironment(environment); } @Test public void testBasePathPlaceHolder() { Mockito.when(environment.getProperty("var")).thenReturn("varValue"); PojoSwaggerGenerator swaggerGenerator = new PojoSwaggerGenerator(null); swaggerGenerator.setBasePath("/a/${var}/b"); Assertions.assertEquals("/a/varValue/b", SwaggerUtils.getBasePath(swaggerGenerator.getOpenAPI())); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core; import static junit.framework.TestCase.fail; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.when; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils; import org.apache.servicecomb.swagger.generator.core.pojo.TestTypeClass; import org.apache.servicecomb.swagger.generator.core.pojo.TestTypeRecord; import org.apache.servicecomb.swagger.generator.core.pojo.TestType1; import org.apache.servicecomb.swagger.generator.core.pojo.TestType2; import org.apache.servicecomb.swagger.generator.core.schema.RepeatOperation; import org.apache.servicecomb.swagger.generator.core.schema.Schema; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.StringSchema; import io.swagger.v3.oas.models.parameters.RequestBody; @SuppressWarnings("rawtypes") public class TestSwaggerUtils { Logger LOGGER = Logger.getLogger(TestSwaggerUtils.class.getName()); @Test public void testSchemaMethod() { UnitTestSwaggerUtils.testSwagger("schemas/Schema.yaml", Schema.class); } @Test public void testRepeatOperation() { UnitTestSwaggerUtils.testException( "OperationId must be unique. method=org.apache.servicecomb.swagger.generator.core.schema.RepeatOperation:add.", RepeatOperation.class); } @Test public void noParameterName() { Method method = ReflectUtils.findMethod(Schema.class, "testint"); Parameter parameter = Mockito.spy(method.getParameters()[0]); Mockito.when(parameter.isNamePresent()).thenReturn(false); IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> SwaggerGeneratorUtils.collectParameterName(parameter)); String expectedMsg = "parameter name is not present, method=org.apache.servicecomb.swagger.generator.core.schema.Schema:testint\n" + "solution:\n" + " change pom.xml, add compiler argument: -parameters, for example:\n" + " \n" + " org.apache.maven.plugins\n" + " maven-compiler-plugin\n" + " \n" + " -parameters\n" + " \n" + " "; Assertions.assertEquals(expectedMsg, exception.getMessage()); } @Test public void testGetRawJsonType() { RequestBody param = Mockito.mock(RequestBody.class); Map extensions = new HashMap<>(); when(param.getExtensions()).thenReturn(extensions); extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, true); Assertions.assertTrue(SwaggerUtils.isRawJsonType(param)); extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, "test"); Assertions.assertFalse(SwaggerUtils.isRawJsonType(param)); } private static class AllTypeTest1 { TestType1 t1; List t2; Map t3; TestType1[] t4; } private static class AllTypeTest2 { TestType2 t1; List t2; Map t3; TestType2[] t4; } private static class AllTypeTest3{ TestTypeRecord t5; } private static class AllTypeTest4{ TestTypeClass t5; } @Test public void testAddDefinitions() { testAllType(AllTypeTest1.class, AllTypeTest2.class); } private void testAllType(Class clazz1, Class clazz2) { Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz2.getDeclaredFields(); for (Field value : fields1) { for (Field field : fields2) { if (value.isSynthetic() || field.isSynthetic()) { continue; } try { testExcep(value.getGenericType(), field.getGenericType()); fail("IllegalArgumentException expected"); } catch (IllegalArgumentException e) { LOGGER.warning(value.getGenericType() + " " + field.getGenericType() + " " + e.getMessage()); MatcherAssert.assertThat(e.getMessage(), containsString("duplicate param model:")); } } } } @Test public void testAddDefinitionsWithRecord() { testAllType(AllTypeTest3.class, AllTypeTest4.class); } private void testExcep(Type f1, Type f2) { OpenAPI swagger = new OpenAPI(); SwaggerUtils.resolveTypeSchemas(swagger, f1); SwaggerUtils.resolveTypeSchemas(swagger, f2); } @Test public void test_resolve_type_schemas_correct() { OpenAPI openAPI = new OpenAPI(); io.swagger.v3.oas.models.media.Schema schema = SwaggerUtils.resolveTypeSchemas(openAPI, String.class); Assertions.assertTrue(schema instanceof StringSchema); openAPI = new OpenAPI(); schema = SwaggerUtils.resolveTypeSchemas(openAPI, Integer.class); Assertions.assertTrue(schema instanceof IntegerSchema); openAPI = new OpenAPI(); schema = SwaggerUtils.resolveTypeSchemas(openAPI, TestType1.class); schema = SwaggerUtils.getSchema(openAPI, schema); // resolve reference // should be ObjectSchema but swagger is not. //
 Assertions.assertTrue(schema instanceof ObjectSchema) 
Assertions.assertEquals("object", schema.getType()); openAPI = new OpenAPI(); schema = SwaggerUtils.getSchema(openAPI, SwaggerUtils.resolveTypeSchemas(openAPI, TestTypeClass.class)); // resolve reference Assertions.assertEquals("object", schema.getType()); openAPI = new OpenAPI(); schema = SwaggerUtils.getSchema(openAPI, SwaggerUtils.resolveTypeSchemas(openAPI, TestTypeRecord.class)); // resolve reference Assertions.assertEquals("object", schema.getType()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/model/TestSwaggerOperations.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.model; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.pojo.PojoExample1; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.parameters.RequestBody; public class TestSwaggerOperations { @Test public void emptyOperationId() { OpenAPI swagger = SwaggerUtils.parseAndValidateSwagger(this.getClass().getResource("/schemas/boolean.yaml")); swagger.getPaths().values().stream() .findFirst().get() .getPost().setOperationId(""); IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> new SwaggerOperations(swagger)); Assertions.assertEquals("OperationId can not be empty, path=/testboolean, httpMethod=POST.", exception.getMessage()); } @Test public void testPojoExample1() { SwaggerOperations swaggerOperations = SwaggerOperations.generate(PojoExample1.class); SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMultiParameter"); Assertions.assertEquals(null, swaggerOperation.getOperation().getParameters()); RequestBody requestBody = swaggerOperation.getOperation().getRequestBody(); Assertions.assertEquals(2, SwaggerUtils.getSchema(swaggerOperation.getSwagger(), requestBody.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE).getSchema()).getProperties().size()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/PojoExample1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.pojo; public class PojoExample1 { public TestType1 testOneParameter(TestType1 testType1) { return null; } public TestType1 testMultiParameter(TestType1 testType1, String testString) { return null; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.pojo; import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "XXX") public class TestType1 { public Integer val1; } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.pojo; import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "XXX") public class TestType2 { public String val2; } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.pojo; import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "YYY") public class TestTypeClass { @Schema(description = "language") private TestTypeEnumLang testTypeEnumLang; public TestTypeEnumLang getTestTypeEnumLang() { return testTypeEnumLang; } public void setTestTypeEnumLang(TestTypeEnumLang testTypeEnumLang) { this.testTypeEnumLang = testTypeEnumLang; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.pojo; public enum TestTypeEnumLang { JAVA, CHINESE, UNKNOWN; } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.pojo; import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "YYY") public record TestTypeRecord( @Schema(description = "language") TestTypeEnumLang testTypeEnumLang) { } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Contact; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.models.OpenAPI; public class OpenAPIDefinitionProcessorTest { @OpenAPIDefinition(tags = { @Tag(name = "testTag", description = "desc", externalDocs = @ExternalDocumentation(description = "testValue", url = "testUrl")) }, servers = {@Server(url = "127.0.0.1")}, info = @Info(title = "title", version = "version", description = "desc", contact = @Contact(name = "contactName"), license = @License(name = "licenseName"))) private static class SwaggerTestTarget { } @Test public void testProcess() { OpenAPI swagger = SwaggerGenerator.generate(SwaggerTestTarget.class); Assertions.assertEquals(1, swagger.getTags().size()); io.swagger.v3.oas.models.tags.Tag tag = swagger.getTags().get(0); Assertions.assertEquals("testTag", tag.getName()); Assertions.assertEquals("desc", tag.getDescription()); Assertions.assertEquals("testValue", tag.getExternalDocs().getDescription()); Assertions.assertEquals("testUrl", tag.getExternalDocs().getUrl()); Assertions.assertEquals("127.0.0.1", swagger.getServers().get(0).getUrl()); io.swagger.v3.oas.models.info.Info info = swagger.getInfo(); Assertions.assertEquals("title", info.getTitle()); Assertions.assertEquals("version", info.getVersion()); Assertions.assertEquals("desc", info.getDescription()); Assertions.assertEquals("contactName", info.getContact().getName()); Assertions.assertEquals("licenseName", info.getLicense().getName()); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.processor.annotation; import static org.hamcrest.Matchers.containsInAnyOrder; import java.util.Map; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.models.Components; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.core.MediaType; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class OperationMethodAnnotationProcessorTest { SwaggerOperations swaggerOperations; @BeforeAll public void setUp() { swaggerOperations = SwaggerOperations.generate(TestClass.class); } @AfterAll public void tearDown() { swaggerOperations = null; } private static class TestClass { @Operation(summary = "value1", tags = {"tag1", "tag2"}) public void function() { } @Operation(summary = "value2") public void functionWithNoTag() { } // mediaType will be ignored @Operation(summary = "testSingleMediaType", responses = {@ApiResponse(responseCode = "200", content = @Content( mediaType = MediaType.TEXT_PLAIN, schema = @Schema(implementation = String.class)))}) public String testSingleMediaType(String input) { return input; } @Operation(summary = "testMultiMediaType", responses = { @ApiResponse(responseCode = "200", content = { @Content(mediaType = MediaType.APPLICATION_JSON), @Content(mediaType = MediaType.TEXT_PLAIN) })}) public String testMultiMediaType(String input) { return input; } @Operation(summary = "testBlankMediaType", responses = {@ApiResponse(responseCode = "200", content = @Content(mediaType = ""))}, requestBody = @RequestBody(content = @Content(mediaType = "", schema = @Schema(implementation = String.class)))) public String testBlankMediaType(String input) { return input; } @Operation(summary = "testBodyParam") public String testBodyParam(@RequestBody(content = @Content( schema = @Schema( implementation = TestBodyBean.class))) TestBodyBean user) { return user.toString(); } } private static class TestBodyBean { @NotBlank private String age; @NotNull private String name; @NotEmpty private String sexes; public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSexes() { return sexes; } public void setSexes(String sexes) { this.sexes = sexes; } } @Test public void testConvertTags() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("function"); MatcherAssert.assertThat(swaggerOperation.getOperation().getTags(), containsInAnyOrder("tag1", "tag2")); } @Test public void testConvertTagsOnMethodWithNoTag() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("functionWithNoTag"); Assertions.assertNull(swaggerOperation.getOperation().getTags()); } @Test public void testMultiMediaType() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMultiMediaType"); MatcherAssert.assertThat(swaggerOperation.getOperation().getRequestBody().getContent().keySet(), Matchers.contains(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN)); MatcherAssert.assertThat(swaggerOperation.getOperation().getResponses().get("200").getContent().keySet(), Matchers.contains(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)); } @Test public void testSingleMediaType() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testSingleMediaType"); MatcherAssert.assertThat(swaggerOperation.getOperation() .getRequestBody().getContent().get(MediaType.APPLICATION_JSON).getSchema().get$ref(), Matchers.equalTo(Components.COMPONENTS_SCHEMAS_REF + "testSingleMediaTypeBody")); MatcherAssert.assertThat(swaggerOperation.getOperation() .getResponses().get("200").getContent().get(MediaType.TEXT_PLAIN).getSchema().getType(), Matchers.equalTo("string")); } @Test public void testBlankMediaType() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBlankMediaType"); MatcherAssert.assertThat(swaggerOperation.getOperation().getRequestBody().getContent().keySet(), Matchers.contains(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN)); MatcherAssert.assertThat(swaggerOperation.getOperation().getResponses() .get(SwaggerConst.SUCCESS_KEY).getContent().keySet(), Matchers.contains(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN)); } @Test public void testBodyParam() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBodyParam"); io.swagger.v3.oas.models.media.Schema schema = swaggerOperation.getSwagger() .getPaths().get("/testBodyParam").getPost().getRequestBody().getContent() .get(MediaType.APPLICATION_JSON).getSchema(); Assertions.assertEquals(Components.COMPONENTS_SCHEMAS_REF + "TestBodyBean", schema.get$ref()); schema = swaggerOperation.getSwagger().getComponents().getSchemas().get("TestBodyBean"); Map properties = schema.getProperties(); Assertions.assertEquals(properties.get("age").getType(), "string"); Assertions.assertEquals(properties.get("sexes").getType(), "string"); Assertions.assertEquals(properties.get("name").getType(), "string"); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/AllType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.schema; import java.util.List; import org.apache.servicecomb.foundation.test.scaffolding.model.User; public class AllType { // public boolean bValue; // // public byte byteValue; // // public Byte byteObjectValue; // // public short sValue; // // public Short sObjectValue; // // public int iValue; // // public Integer iObjectValue; // // public long lValue; // // public Long lObjectValue; // // public float fValue; // // public Float fObjectValue; // // public double dValue; // // public Double dObjectValue; // // public Color enumValue; // // public char cValue; // // public Character cObjectValue; // // public byte[] bytes; // // public String strValue; // // public Set set; public List list; // public Map map; } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/ArrayType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.schema; import org.apache.servicecomb.foundation.test.scaffolding.model.User; public class ArrayType { public byte[] testBytes(byte[] value) { return null; } public User[] testUsers(User[] value) { return null; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/RepeatOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.schema; public class RepeatOperation { public int add(int x, int y) { return x + y; } public int add(int x, int y, int z) { return x + y + z; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/Schema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.schema; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.servlet.http.HttpServletRequest; @SuppressWarnings("all") public class Schema { @Operation(method = "", hidden = true) public void hidden() { } @ApiResponse(headers = {@Header(name = "h", schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = String.class))}, responseCode = "200", description = "") public void testApiResponse() { } @ApiResponse(headers = {@Header(name = "h", schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = String.class))}, description = "") public void testApiOperation() { } @ApiResponse(headers = {@Header(name = "h", schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = String.class))}) public void testResponseHeader() { } public void testboolean(boolean value) { } public void testBoolean(Boolean value) { } public void testbyte(byte value) { } public void testByte(Byte value) { } public void testshort(short value) { } public void testShort(Short value) { } public void testint(int value) { } public void testInteger(Integer value) { } public void testlong(long value) { } public void testLong(Long value) { } public void testfloat(float value) { } public void testFloat(Float value) { } public void testdouble(double value) { } public void testDouble(Double value) { } public void testOneEnum(Color color) { } public void testEnum(Color color, Color color1) { } public void testchar(char value) { } public void testChar(Character value) { } public void testbytes(byte[] value) { } public void testBytes(Byte[] value) { } public void testString(String value) { } public void testObject(User user) { } public void testArray(String[] value) { } public void testSet(Set value) { } public List> nestedListString(List> param) { return param; } public void testList(List value) { } public void testMap(Map value) { } public Date testDate() { return null; } public void testMapList(Map> value) { } public CompletableFuture testCompletableFuture() { return null; } public Optional testOptional() { return Optional.empty(); } public CompletableFuture> testCompletableFutureOptional() { return null; } public void testAllType(AllType obj) { } public List testMultiParam(AllType obj, boolean bValue, byte byteValue, short sValue, int iValue, long lValue, float fValue, double dValue, Color enumValue, char cValue, byte[] bytes, String strValue, String[] strArray, Set set, List list, Map map) { return Collections.emptyList(); } public void wrapToBodyWithDesc(@Parameter(name = "desc") int value) { } public void ignoreRequest(HttpServletRequest request, int value) { } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/TestMethodUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils; import java.lang.reflect.Method; import java.util.List; import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.AbstractBaseClass; import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.BaseInterface; import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.Hello2Endpoint; import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.HelloEndpoint; import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.ServiceInterface; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMethodUtils { @Test public void testGetClassMethods() throws Exception { List methods = MethodUtils.findSwaggerMethods(Hello2Endpoint.class); Assertions.assertEquals(3, methods.size()); Assertions.assertEquals(Hello2Endpoint.class, methods.get(0).getDeclaringClass()); Assertions.assertEquals(Hello2Endpoint.class, methods.get(1).getDeclaringClass()); Assertions.assertEquals(Hello2Endpoint.class, methods.get(2).getDeclaringClass()); methods = MethodUtils.findSwaggerMethods(HelloEndpoint.class); Assertions.assertEquals(2, methods.size()); Assertions.assertEquals(HelloEndpoint.class, methods.get(0).getDeclaringClass()); // get Assertions.assertEquals(AbstractBaseClass.class, methods.get(1).getDeclaringClass()); // getBase methods = MethodUtils.findSwaggerMethods(ServiceInterface.class); Assertions.assertEquals(3, methods.size()); Assertions.assertEquals(BaseInterface.class, methods.get(0).getDeclaringClass()); // get Assertions.assertEquals(BaseInterface.class, methods.get(1).getDeclaringClass()); // getArray Assertions.assertEquals(ServiceInterface.class, methods.get(2).getDeclaringClass()); // getBase } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBaseClass.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public abstract class AbstractBaseClass { abstract T get(T param); public T getBase(T param) { return param; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public abstract class AbstractBean { private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/BaseInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public interface BaseInterface { T get(T param); T[] getArray(T[] param); } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/Hello2Endpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public class Hello2Endpoint implements ServiceInterface { @Override public HelloBean get(HelloBean param) { return param; } @Override public HelloBean[] getArray(HelloBean[] param) { return new HelloBean[0]; } @Override public HelloBean getBase(HelloBean param) { return param; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public class HelloBean extends AbstractBean { private String hello; public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public class HelloEndpoint extends AbstractBaseClass { @Override public HelloBean get(HelloBean param) { return param; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/ServiceInterface.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; public interface ServiceInterface extends BaseInterface { HelloBean getBase(HelloBean param); } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBaseService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; import java.util.List; public class AbstractBaseService implements IBaseService { private final IBaseService target; protected AbstractBaseService(IBaseService t) { target = t; } @Override public T hello(T a) { return target.hello(a); } @Override public T[] helloBody(T[] a) { return target.helloBody(a); } @Override public List helloList(List a) { return a; } @Override public PersonBean actual(PersonBean bean) { return target.actual(bean); } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; public abstract class AbstractBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IBaseService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; import java.util.List; public interface IBaseService { T hello(T a); T[] helloBody(T[] a); List helloList(List a); PersonBean actual(PersonBean bean); } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; import java.util.List; import org.springframework.web.multipart.MultipartFile; public interface IMyService extends IBaseService { List parentHello(List bean); } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; public interface IMyServiceChild extends IMyService { } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; public interface IMyServiceChild2 extends IMyService, IBaseService { } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.multipart.MultipartFile; public class MyEndpoint extends AbstractBaseService implements IMyService { public MyEndpoint(@Autowired IMyService other) { super(other); } @Override public List parentHello(List bean) { return null; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; import java.util.List; import org.springframework.web.multipart.MultipartFile; public class MyEndpoint2 implements IMyService { @Override public PersonBean hello(PersonBean a) { return null; } @Override public PersonBean[] helloBody(PersonBean[] a) { return new PersonBean[0]; } @Override public List helloList(List a) { return null; } @Override public PersonBean actual(PersonBean bean) { return null; } @Override public List parentHello(List bean) { return null; } } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/PersonBean.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.core.utils.paramUtilsModel; public class PersonBean extends AbstractBean { } ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.TestPojo$ParameterAnnotation version: 1.0.0 servers: - url: /ParameterAnnotation paths: /differentName: post: summary: differentName operationId: differentName requestBody: content: application/json: schema: $ref: "#/components/schemas/differentNameBody" application/protobuf: schema: $ref: "#/components/schemas/differentNameBody" text/plain: schema: $ref: "#/components/schemas/differentNameBody" x-name: differentNameBody responses: "200": description: response of 200 content: application/json: schema: type: integer format: int32 application/protobuf: schema: type: integer format: int32 text/plain: schema: type: integer format: int32 components: schemas: differentNameBody: type: object properties: x: type: integer format: int32 "y": type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /ignoreRequest: post: operationId: ignoreRequest requestBody: content: application/json: schema: $ref: "#/components/schemas/ignoreRequestBody" application/protobuf: schema: $ref: "#/components/schemas/ignoreRequestBody" text/plain: schema: $ref: "#/components/schemas/ignoreRequestBody" x-name: ignoreRequestBody responses: "200": description: response of 200 /nestedListString: post: operationId: nestedListString requestBody: content: application/json: schema: $ref: "#/components/schemas/nestedListStringBody" application/protobuf: schema: $ref: "#/components/schemas/nestedListStringBody" text/plain: schema: $ref: "#/components/schemas/nestedListStringBody" x-name: nestedListStringBody responses: "200": description: response of 200 content: application/json: schema: type: array items: type: array items: type: string application/protobuf: schema: type: array items: type: array items: type: string text/plain: schema: type: array items: type: array items: type: string /testAllType: post: operationId: testAllType requestBody: content: application/json: schema: $ref: "#/components/schemas/AllType" application/protobuf: schema: $ref: "#/components/schemas/AllType" text/plain: schema: $ref: "#/components/schemas/AllType" x-name: obj responses: "200": description: response of 200 /testApiOperation: post: operationId: testApiOperation responses: "200": description: response of 200 headers: h: schema: type: string description: "" nullable: false example: "" /testApiResponse: post: operationId: testApiResponse responses: "200": description: response of 200 headers: h: schema: type: string description: "" nullable: false example: "" /testArray: post: operationId: testArray requestBody: content: application/json: schema: $ref: "#/components/schemas/testArrayBody" application/protobuf: schema: $ref: "#/components/schemas/testArrayBody" text/plain: schema: $ref: "#/components/schemas/testArrayBody" x-name: testArrayBody responses: "200": description: response of 200 /testBoolean: post: operationId: testBoolean requestBody: content: application/json: schema: $ref: "#/components/schemas/testBooleanBody" application/protobuf: schema: $ref: "#/components/schemas/testBooleanBody" text/plain: schema: $ref: "#/components/schemas/testBooleanBody" x-name: testBooleanBody responses: "200": description: response of 200 /testByte: post: operationId: testByte requestBody: content: application/json: schema: $ref: "#/components/schemas/testByteBody" application/protobuf: schema: $ref: "#/components/schemas/testByteBody" text/plain: schema: $ref: "#/components/schemas/testByteBody" x-name: testByteBody responses: "200": description: response of 200 /testBytes: post: operationId: testBytes requestBody: content: application/json: schema: $ref: "#/components/schemas/testBytesBody" application/protobuf: schema: $ref: "#/components/schemas/testBytesBody" text/plain: schema: $ref: "#/components/schemas/testBytesBody" x-name: testBytesBody responses: "200": description: response of 200 /testChar: post: operationId: testChar requestBody: content: application/json: schema: $ref: "#/components/schemas/testCharBody" application/protobuf: schema: $ref: "#/components/schemas/testCharBody" text/plain: schema: $ref: "#/components/schemas/testCharBody" x-name: testCharBody responses: "200": description: response of 200 /testCompletableFuture: post: operationId: testCompletableFuture responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testCompletableFutureOptional: post: operationId: testCompletableFutureOptional responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testDate: post: operationId: testDate responses: "200": description: response of 200 content: application/json: schema: type: string format: date-time application/protobuf: schema: type: string format: date-time text/plain: schema: type: string format: date-time /testDouble: post: operationId: testDouble requestBody: content: application/json: schema: $ref: "#/components/schemas/testDoubleBody" application/protobuf: schema: $ref: "#/components/schemas/testDoubleBody" text/plain: schema: $ref: "#/components/schemas/testDoubleBody" x-name: testDoubleBody responses: "200": description: response of 200 /testEnum: post: operationId: testEnum requestBody: content: application/json: schema: $ref: "#/components/schemas/testEnumBody" application/protobuf: schema: $ref: "#/components/schemas/testEnumBody" text/plain: schema: $ref: "#/components/schemas/testEnumBody" x-name: testEnumBody responses: "200": description: response of 200 /testFloat: post: operationId: testFloat requestBody: content: application/json: schema: $ref: "#/components/schemas/testFloatBody" application/protobuf: schema: $ref: "#/components/schemas/testFloatBody" text/plain: schema: $ref: "#/components/schemas/testFloatBody" x-name: testFloatBody responses: "200": description: response of 200 /testInteger: post: operationId: testInteger requestBody: content: application/json: schema: $ref: "#/components/schemas/testIntegerBody" application/protobuf: schema: $ref: "#/components/schemas/testIntegerBody" text/plain: schema: $ref: "#/components/schemas/testIntegerBody" x-name: testIntegerBody responses: "200": description: response of 200 /testList: post: operationId: testList requestBody: content: application/json: schema: $ref: "#/components/schemas/testListBody" application/protobuf: schema: $ref: "#/components/schemas/testListBody" text/plain: schema: $ref: "#/components/schemas/testListBody" x-name: testListBody responses: "200": description: response of 200 /testLong: post: operationId: testLong requestBody: content: application/json: schema: $ref: "#/components/schemas/testLongBody" application/protobuf: schema: $ref: "#/components/schemas/testLongBody" text/plain: schema: $ref: "#/components/schemas/testLongBody" x-name: testLongBody responses: "200": description: response of 200 /testMap: post: operationId: testMap requestBody: content: application/json: schema: $ref: "#/components/schemas/testMapBody" application/protobuf: schema: $ref: "#/components/schemas/testMapBody" text/plain: schema: $ref: "#/components/schemas/testMapBody" x-name: testMapBody responses: "200": description: response of 200 /testMapList: post: operationId: testMapList requestBody: content: application/json: schema: $ref: "#/components/schemas/testMapListBody" application/protobuf: schema: $ref: "#/components/schemas/testMapListBody" text/plain: schema: $ref: "#/components/schemas/testMapListBody" x-name: testMapListBody responses: "200": description: response of 200 /testMultiParam: post: operationId: testMultiParam requestBody: content: application/json: schema: $ref: "#/components/schemas/testMultiParamBody" application/protobuf: schema: $ref: "#/components/schemas/testMultiParamBody" text/plain: schema: $ref: "#/components/schemas/testMultiParamBody" x-name: testMultiParamBody responses: "200": description: response of 200 content: application/json: schema: type: array items: type: string application/protobuf: schema: type: array items: type: string text/plain: schema: type: array items: type: string /testObject: post: operationId: testObject requestBody: content: application/json: schema: $ref: "#/components/schemas/User" application/protobuf: schema: $ref: "#/components/schemas/User" text/plain: schema: $ref: "#/components/schemas/User" x-name: user responses: "200": description: response of 200 /testOneEnum: post: operationId: testOneEnum requestBody: content: application/json: schema: $ref: "#/components/schemas/testOneEnumBody" application/protobuf: schema: $ref: "#/components/schemas/testOneEnumBody" text/plain: schema: $ref: "#/components/schemas/testOneEnumBody" x-name: testOneEnumBody responses: "200": description: response of 200 /testOptional: post: operationId: testOptional responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testResponseHeader: post: operationId: testResponseHeader responses: "200": description: response of 200 headers: h: schema: type: string description: "" nullable: false example: "" /testSet: post: operationId: testSet requestBody: content: application/json: schema: $ref: "#/components/schemas/testSetBody" application/protobuf: schema: $ref: "#/components/schemas/testSetBody" text/plain: schema: $ref: "#/components/schemas/testSetBody" x-name: testSetBody responses: "200": description: response of 200 /testShort: post: operationId: testShort requestBody: content: application/json: schema: $ref: "#/components/schemas/testShortBody" application/protobuf: schema: $ref: "#/components/schemas/testShortBody" text/plain: schema: $ref: "#/components/schemas/testShortBody" x-name: testShortBody responses: "200": description: response of 200 /testString: post: operationId: testString requestBody: content: application/json: schema: $ref: "#/components/schemas/testStringBody" application/protobuf: schema: $ref: "#/components/schemas/testStringBody" text/plain: schema: $ref: "#/components/schemas/testStringBody" x-name: testStringBody responses: "200": description: response of 200 /testboolean: post: operationId: testboolean requestBody: content: application/json: schema: $ref: "#/components/schemas/testbooleanBody" application/protobuf: schema: $ref: "#/components/schemas/testbooleanBody" text/plain: schema: $ref: "#/components/schemas/testbooleanBody" x-name: testbooleanBody responses: "200": description: response of 200 /testbyte: post: operationId: testbyte requestBody: content: application/json: schema: $ref: "#/components/schemas/testbyteBody" application/protobuf: schema: $ref: "#/components/schemas/testbyteBody" text/plain: schema: $ref: "#/components/schemas/testbyteBody" x-name: testbyteBody responses: "200": description: response of 200 /testbytes: post: operationId: testbytes requestBody: content: application/json: schema: $ref: "#/components/schemas/testbytesBody" application/protobuf: schema: $ref: "#/components/schemas/testbytesBody" text/plain: schema: $ref: "#/components/schemas/testbytesBody" x-name: testbytesBody responses: "200": description: response of 200 /testchar: post: operationId: testchar requestBody: content: application/json: schema: $ref: "#/components/schemas/testcharBody" application/protobuf: schema: $ref: "#/components/schemas/testcharBody" text/plain: schema: $ref: "#/components/schemas/testcharBody" x-name: testcharBody responses: "200": description: response of 200 /testdouble: post: operationId: testdouble requestBody: content: application/json: schema: $ref: "#/components/schemas/testdoubleBody" application/protobuf: schema: $ref: "#/components/schemas/testdoubleBody" text/plain: schema: $ref: "#/components/schemas/testdoubleBody" x-name: testdoubleBody responses: "200": description: response of 200 /testfloat: post: operationId: testfloat requestBody: content: application/json: schema: $ref: "#/components/schemas/testfloatBody" application/protobuf: schema: $ref: "#/components/schemas/testfloatBody" text/plain: schema: $ref: "#/components/schemas/testfloatBody" x-name: testfloatBody responses: "200": description: response of 200 /testint: post: operationId: testint requestBody: content: application/json: schema: $ref: "#/components/schemas/testintBody" application/protobuf: schema: $ref: "#/components/schemas/testintBody" text/plain: schema: $ref: "#/components/schemas/testintBody" x-name: testintBody responses: "200": description: response of 200 /testlong: post: operationId: testlong requestBody: content: application/json: schema: $ref: "#/components/schemas/testlongBody" application/protobuf: schema: $ref: "#/components/schemas/testlongBody" text/plain: schema: $ref: "#/components/schemas/testlongBody" x-name: testlongBody responses: "200": description: response of 200 /testshort: post: operationId: testshort requestBody: content: application/json: schema: $ref: "#/components/schemas/testshortBody" application/protobuf: schema: $ref: "#/components/schemas/testshortBody" text/plain: schema: $ref: "#/components/schemas/testshortBody" x-name: testshortBody responses: "200": description: response of 200 /wrapToBodyWithDesc: post: operationId: wrapToBodyWithDesc requestBody: content: application/json: schema: $ref: "#/components/schemas/wrapToBodyWithDescBody" application/protobuf: schema: $ref: "#/components/schemas/wrapToBodyWithDescBody" text/plain: schema: $ref: "#/components/schemas/wrapToBodyWithDescBody" x-name: wrapToBodyWithDescBody responses: "200": description: response of 200 components: schemas: ignoreRequestBody: type: object properties: value: type: integer format: int32 nestedListStringBody: type: object properties: param: type: array items: type: array items: type: string AllType: type: object properties: list: type: array items: $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.swagger.generator.core.schema.AllType User: type: object properties: name: type: string friends: type: array items: $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User testArrayBody: type: object properties: value: type: array items: type: string testBooleanBody: type: object properties: value: type: boolean testByteBody: type: object properties: value: type: integer format: int32 testBytesBody: type: object properties: value: type: string format: byte testCharBody: type: object properties: value: type: string testDoubleBody: type: object properties: value: type: number format: double testEnumBody: type: object properties: color: type: string enum: - RED - YELLOW - BLUE color1: type: string enum: - RED - YELLOW - BLUE testFloatBody: type: object properties: value: type: number format: float testIntegerBody: type: object properties: value: type: integer format: int32 testListBody: type: object properties: value: type: array items: $ref: "#/components/schemas/User" testLongBody: type: object properties: value: type: integer format: int64 testMapBody: type: object properties: value: type: object additionalProperties: $ref: "#/components/schemas/User" testMapListBody: type: object properties: value: type: object additionalProperties: type: array items: $ref: "#/components/schemas/User" testMultiParamBody: type: object properties: obj: $ref: "#/components/schemas/AllType" bValue: type: boolean byteValue: type: integer format: int32 sValue: type: integer format: int32 iValue: type: integer format: int32 lValue: type: integer format: int64 fValue: type: number format: float dValue: type: number format: double enumValue: type: string enum: - RED - YELLOW - BLUE cValue: type: string bytes: type: string format: byte strValue: type: string strArray: type: array items: type: string set: uniqueItems: true type: array items: type: string list: type: array items: $ref: "#/components/schemas/User" map: type: object additionalProperties: $ref: "#/components/schemas/User" testOneEnumBody: type: object properties: color: type: string enum: - RED - YELLOW - BLUE testSetBody: type: object properties: value: uniqueItems: true type: array items: type: string testShortBody: type: object properties: value: type: integer format: int32 testStringBody: type: object properties: value: type: string testbooleanBody: type: object properties: value: type: boolean testbyteBody: type: object properties: value: type: integer format: int32 testbytesBody: type: object properties: value: type: string format: byte testcharBody: type: object properties: value: type: string testdoubleBody: type: object properties: value: type: number format: double testfloatBody: type: object properties: value: type: number format: float testintBody: type: object properties: value: type: integer format: int32 testlongBody: type: object properties: value: type: integer format: int64 testshortBody: type: object properties: value: type: integer format: int32 wrapToBodyWithDescBody: type: object properties: desc: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /ignoreRequest: post: operationId: ignoreRequest requestBody: content: application/json: schema: $ref: '#/components/schemas/ignoreRequestBody' responses: "200": description: response of 200 content: application/json: {} /nestedListString: post: operationId: nestedListString requestBody: content: application/json: schema: $ref: '#/components/schemas/nestedListStringBody' responses: "200": description: response of 200 content: application/json: schema: type: array items: type: array items: type: string /part: post: operationId: part requestBody: content: multipart/form-data: schema: type: string format: binary responses: "200": description: response of 200 content: application/json: {} /partArray: post: operationId: partArray requestBody: content: multipart/form-data: schema: type: array items: type: string format: binary responses: "200": description: response of 200 content: application/json: {} /partList: post: operationId: partList requestBody: content: multipart/form-data: schema: type: array items: type: string format: binary responses: "200": description: response of 200 content: application/json: {} /testAllType: post: operationId: testAllType responses: "200": description: response of 200 content: application/json: {} /testApiOperation: post: operationId: testApiOperation responses: default: description: response of default headers: h: description: "" schema: type: string description: "" format: "" content: {} "200": description: response of 200 content: application/json: {} /testApiResponse: post: operationId: testApiResponse responses: "200": description: response of 200 headers: h: description: "" schema: type: string description: "" format: "" content: application/json: {} /testArray: post: operationId: testArray requestBody: content: application/json: schema: $ref: '#/components/schemas/testArrayBody' responses: "200": description: response of 200 content: application/json: {} /testBoolean: post: operationId: testBoolean requestBody: content: application/json: schema: $ref: '#/components/schemas/testBooleanBody' responses: "200": description: response of 200 content: application/json: {} /testByte: post: operationId: testByte requestBody: content: application/json: schema: $ref: '#/components/schemas/testByteBody' responses: "200": description: response of 200 content: application/json: {} /testBytes: post: operationId: testBytes requestBody: content: application/json: schema: $ref: '#/components/schemas/testBytesBody' responses: "200": description: response of 200 content: application/json: {} /testChar: post: operationId: testChar requestBody: content: application/json: schema: $ref: '#/components/schemas/testCharBody' responses: "200": description: response of 200 content: application/json: {} /testCompletableFuture: post: operationId: testCompletableFuture responses: "200": description: response of 200 content: application/json: schema: type: string /testCompletableFutureOptional: post: operationId: testCompletableFutureOptional responses: "200": description: response of 200 content: application/json: schema: type: string /testDate: post: operationId: testDate responses: "200": description: response of 200 content: application/json: schema: type: string format: date-time /testDouble: post: operationId: testDouble requestBody: content: application/json: schema: $ref: '#/components/schemas/testDoubleBody' responses: "200": description: response of 200 content: application/json: {} /testEnum: post: operationId: testEnum requestBody: content: application/json: schema: $ref: '#/components/schemas/testEnumBody' responses: "200": description: response of 200 content: application/json: {} /testFloat: post: operationId: testFloat requestBody: content: application/json: schema: $ref: '#/components/schemas/testFloatBody' responses: "200": description: response of 200 content: application/json: {} /testInteger: post: operationId: testInteger requestBody: content: application/json: schema: $ref: '#/components/schemas/testIntegerBody' responses: "200": description: response of 200 content: application/json: {} /testList: post: operationId: testList requestBody: content: application/json: schema: $ref: '#/components/schemas/testListBody' responses: "200": description: response of 200 content: application/json: {} /testLong: post: operationId: testLong requestBody: content: application/json: schema: $ref: '#/components/schemas/testLongBody' responses: "200": description: response of 200 content: application/json: {} /testMap: post: operationId: testMap requestBody: content: application/json: schema: $ref: '#/components/schemas/testMapBody' responses: "200": description: response of 200 content: application/json: {} /testMapList: post: operationId: testMapList requestBody: content: application/json: schema: $ref: '#/components/schemas/testMapListBody' responses: "200": description: response of 200 content: application/json: {} /testMultiParam: post: operationId: testMultiParam requestBody: content: application/json: schema: $ref: '#/components/schemas/testMultiParamBody' responses: "200": description: response of 200 content: application/json: schema: type: array items: type: string /testObject: post: operationId: testObject responses: "200": description: response of 200 content: application/json: {} /testOneEnum: post: operationId: testOneEnum requestBody: content: application/json: schema: $ref: '#/components/schemas/testOneEnumBody' responses: "200": description: response of 200 content: application/json: {} /testOptional: post: operationId: testOptional responses: "200": description: response of 200 content: application/json: schema: type: string /testResponseHeader: post: operationId: testResponseHeader responses: default: description: response of default headers: h: description: "" schema: type: string description: "" format: "" content: {} "200": description: response of 200 content: application/json: {} /testSet: post: operationId: testSet requestBody: content: application/json: schema: $ref: '#/components/schemas/testSetBody' responses: "200": description: response of 200 content: application/json: {} /testShort: post: operationId: testShort requestBody: content: application/json: schema: $ref: '#/components/schemas/testShortBody' responses: "200": description: response of 200 content: application/json: {} /testString: post: operationId: testString requestBody: content: application/json: schema: $ref: '#/components/schemas/testStringBody' responses: "200": description: response of 200 content: application/json: {} /testboolean: post: operationId: testboolean requestBody: content: application/json: schema: $ref: '#/components/schemas/testbooleanBody' responses: "200": description: response of 200 content: application/json: {} /testbyte: post: operationId: testbyte requestBody: content: application/json: schema: $ref: '#/components/schemas/testbyteBody' responses: "200": description: response of 200 content: application/json: {} /testbytes: post: operationId: testbytes requestBody: content: application/json: schema: $ref: '#/components/schemas/testbytesBody' responses: "200": description: response of 200 content: application/json: {} /testchar: post: operationId: testchar requestBody: content: application/json: schema: $ref: '#/components/schemas/testcharBody' responses: "200": description: response of 200 content: application/json: {} /testdouble: post: operationId: testdouble requestBody: content: application/json: schema: $ref: '#/components/schemas/testdoubleBody' responses: "200": description: response of 200 content: application/json: {} /testfloat: post: operationId: testfloat requestBody: content: application/json: schema: $ref: '#/components/schemas/testfloatBody' responses: "200": description: response of 200 content: application/json: {} /testint: post: operationId: testint requestBody: content: application/json: schema: $ref: '#/components/schemas/testintBody' responses: "200": description: response of 200 content: application/json: {} /testlong: post: operationId: testlong requestBody: content: application/json: schema: $ref: '#/components/schemas/testlongBody' responses: "200": description: response of 200 content: application/json: {} /testshort: post: operationId: testshort requestBody: content: application/json: schema: $ref: '#/components/schemas/testshortBody' responses: "200": description: response of 200 content: application/json: {} /wrapToBodyWithDesc: post: operationId: wrapToBodyWithDesc requestBody: content: application/json: schema: $ref: '#/components/schemas/wrapToBodyWithDescBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: ignoreRequestBody: type: object properties: value: type: integer format: int32 nestedListStringBody: type: object properties: param: type: array items: type: array items: type: string AllType: type: object properties: list: type: array items: $ref: '#/components/schemas/User' User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' testArrayBody: type: object properties: value: type: array items: type: string testBooleanBody: type: object properties: value: type: boolean testByteBody: type: object properties: value: type: string format: byte testBytesBody: type: object properties: value: type: array items: type: string format: byte testCharBody: type: object properties: value: type: string testDoubleBody: type: object properties: value: type: number format: double testEnumBody: type: object properties: color: type: string enum: - RED - YELLOW - BLUE color1: type: string enum: - RED - YELLOW - BLUE testFloatBody: type: object properties: value: type: number format: float testIntegerBody: type: object properties: value: type: integer format: int32 testListBody: type: object properties: value: type: array items: $ref: '#/components/schemas/User' testLongBody: type: object properties: value: type: integer format: int64 testMapBody: type: object properties: value: type: object additionalProperties: $ref: '#/components/schemas/User' testMapListBody: type: object properties: value: type: object additionalProperties: type: array items: $ref: '#/components/schemas/User' testMultiParamBody: type: object properties: obj: $ref: '#/components/schemas/AllType' bValue: type: boolean byteValue: type: string format: byte sValue: type: integer format: int32 iValue: type: integer format: int32 lValue: type: integer format: int64 fValue: type: number format: float dValue: type: number format: double enumValue: type: string enum: - RED - YELLOW - BLUE cValue: type: string bytes: type: array items: type: string format: byte strValue: type: string strArray: type: array items: type: string set: uniqueItems: true type: array items: type: string list: type: array items: $ref: '#/components/schemas/User' map: type: object additionalProperties: $ref: '#/components/schemas/User' testOneEnumBody: type: object properties: color: type: string enum: - RED - YELLOW - BLUE testSetBody: type: object properties: value: uniqueItems: true type: array items: type: string testShortBody: type: object properties: value: type: integer format: int32 testStringBody: type: object properties: value: type: string testbooleanBody: type: object properties: value: type: boolean testbyteBody: type: object properties: value: type: string format: byte testbytesBody: type: object properties: value: type: array items: type: string format: byte testcharBody: type: object properties: value: type: string testdoubleBody: type: object properties: value: type: number format: double testfloatBody: type: object properties: value: type: number format: float testintBody: type: object properties: value: type: integer format: int32 testlongBody: type: object properties: value: type: integer format: int64 testshortBody: type: object properties: value: type: integer format: int32 wrapToBodyWithDescBody: type: object properties: desc: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testAllType: post: operationId: testAllType responses: "200": description: response of 200 content: application/json: {} components: schemas: AllType: type: object properties: list: type: array items: $ref: '#/components/schemas/User' User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testApiOperation: post: operationId: testApiOperation responses: default: description: response of default headers: h: description: "" schema: type: string description: "" format: "" content: {} "200": description: response of 200 content: application/json: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testApiResponse: post: operationId: testApiResponse responses: "200": description: response of 200 headers: h: description: "" schema: type: string description: "" format: "" content: application/json: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testArray: post: operationId: testArray requestBody: content: application/json: schema: $ref: '#/components/schemas/testArrayBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testArrayBody: type: object properties: value: type: array items: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testboolean: post: operationId: testboolean requestBody: content: application/json: schema: $ref: '#/components/schemas/testbooleanBody' x-name: name responses: "200": description: response of 200 content: application/json: {} components: schemas: testbooleanBody: type: object properties: value: type: boolean ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testBoolean: post: operationId: testBoolean requestBody: content: application/json: schema: $ref: '#/components/schemas/testBooleanBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testBooleanBody: type: object properties: value: type: boolean ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testbyte: post: operationId: testbyte requestBody: content: application/json: schema: $ref: '#/components/schemas/testbyteBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testbyteBody: type: object properties: value: type: string format: byte ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testByte: post: operationId: testByte requestBody: content: application/json: schema: $ref: '#/components/schemas/testByteBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testByteBody: type: object properties: value: type: string format: byte ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testbytes: post: operationId: testbytes requestBody: content: application/json: schema: $ref: '#/components/schemas/testbytesBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testbytesBody: type: object properties: value: type: array items: type: string format: byte ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testBytes: post: operationId: testBytes requestBody: content: application/json: schema: $ref: '#/components/schemas/testBytesBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testBytesBody: type: object properties: value: type: array items: type: string format: byte ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testchar: post: operationId: testchar requestBody: content: application/json: schema: $ref: '#/components/schemas/testcharBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testcharBody: type: object properties: value: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testChar: post: operationId: testChar requestBody: content: application/json: schema: $ref: '#/components/schemas/testCharBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testCharBody: type: object properties: value: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testCompletableFuture: post: operationId: testCompletableFuture responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testDate: post: operationId: testDate responses: "200": description: response of 200 content: application/json: schema: type: string format: date-time components: schemas: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testdouble: post: operationId: testdouble requestBody: content: application/json: schema: $ref: '#/components/schemas/testdoubleBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testdoubleBody: type: object properties: value: type: number format: double ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testDouble: post: operationId: testDouble requestBody: content: application/json: schema: $ref: '#/components/schemas/testDoubleBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testDoubleBody: type: object properties: value: type: number format: double ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testEnum: post: operationId: testEnum requestBody: content: application/json: schema: $ref: '#/components/schemas/testEnumBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testEnumBody: type: object properties: color: type: string enum: - RED - YELLOW - BLUE color1: type: string enum: - RED - YELLOW - BLUE ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testfloat: post: operationId: testfloat requestBody: content: application/json: schema: $ref: '#/components/schemas/testfloatBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testfloatBody: type: object properties: value: type: number format: float ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testFloat: post: operationId: testFloat requestBody: content: application/json: schema: $ref: '#/components/schemas/testFloatBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testFloatBody: type: object properties: value: type: number format: float ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /ignoreRequest: post: operationId: ignoreRequest requestBody: content: application/json: schema: $ref: '#/components/schemas/ignoreRequestBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: ignoreRequestBody: type: object properties: value: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testint: post: operationId: testint requestBody: content: application/json: schema: $ref: '#/components/schemas/testintBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testintBody: type: object properties: value: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testInteger: post: operationId: testInteger requestBody: content: application/json: schema: $ref: '#/components/schemas/testIntegerBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testIntegerBody: type: object properties: value: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testList: post: operationId: testList requestBody: content: application/json: schema: $ref: '#/components/schemas/testListBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' testListBody: type: object properties: value: type: array items: $ref: '#/components/schemas/User' ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testlong: post: operationId: testlong requestBody: content: application/json: schema: $ref: '#/components/schemas/testlongBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testlongBody: type: object properties: value: type: integer format: int64 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testLong: post: operationId: testLong requestBody: content: application/json: schema: $ref: '#/components/schemas/testLongBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testLongBody: type: object properties: value: type: integer format: int64 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testMap: post: operationId: testMap requestBody: content: application/json: schema: $ref: '#/components/schemas/testMapBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' testMapBody: type: object properties: value: type: object additionalProperties: $ref: '#/components/schemas/User' ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testMapList: post: operationId: testMapList requestBody: content: application/json: schema: $ref: '#/components/schemas/testMapListBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' testMapListBody: type: object properties: value: type: object additionalProperties: type: array items: $ref: '#/components/schemas/User' ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testMultiParam: post: operationId: testMultiParam requestBody: content: application/json: schema: $ref: '#/components/schemas/testMultiParamBody' responses: "200": description: response of 200 content: application/json: schema: type: array items: type: string components: schemas: AllType: type: object properties: list: type: array items: $ref: '#/components/schemas/User' User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' testMultiParamBody: type: object properties: obj: $ref: '#/components/schemas/AllType' bValue: type: boolean byteValue: type: string format: byte sValue: type: integer format: int32 iValue: type: integer format: int32 lValue: type: integer format: int64 fValue: type: number format: float dValue: type: number format: double enumValue: type: string enum: - RED - YELLOW - BLUE cValue: type: string bytes: type: array items: type: string format: byte strValue: type: string strArray: type: array items: type: string set: uniqueItems: true type: array items: type: string list: type: array items: $ref: '#/components/schemas/User' map: type: object additionalProperties: $ref: '#/components/schemas/User' ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /nestedListString: post: operationId: nestedListString requestBody: content: application/json: schema: $ref: '#/components/schemas/nestedListStringBody' responses: "200": description: response of 200 content: application/json: schema: type: array items: type: array items: type: string components: schemas: nestedListStringBody: type: object properties: param: type: array items: type: array items: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testObject: post: operationId: testObject responses: "200": description: response of 200 content: application/json: {} components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: '#/components/schemas/User' ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testOneEnum: post: operationId: testOneEnum requestBody: content: application/json: schema: $ref: '#/components/schemas/testOneEnumBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testOneEnumBody: type: object properties: color: type: string enum: - RED - YELLOW - BLUE ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /part: post: operationId: part requestBody: content: multipart/form-data: schema: type: string format: binary responses: "200": description: response of 200 content: application/json: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /partArray: post: operationId: partArray requestBody: content: multipart/form-data: schema: type: array items: type: string format: binary responses: "200": description: response of 200 content: application/json: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /partList: post: operationId: partList requestBody: content: multipart/form-data: schema: type: array items: type: string format: binary responses: "200": description: response of 200 content: application/json: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.pojo.PojoExample1 version: 1.0.0 servers: - url: /PojoExample1 paths: /testMultiParameter: post: operationId: testMultiParameter requestBody: content: application/json: schema: $ref: "#/components/schemas/testMultiParameterBody" application/protobuf: schema: $ref: "#/components/schemas/testMultiParameterBody" text/plain: schema: $ref: "#/components/schemas/testMultiParameterBody" x-name: testMultiParameterBody responses: "200": description: response of 200 content: application/json: schema: $ref: "#/components/schemas/XXX" application/protobuf: schema: $ref: "#/components/schemas/XXX" text/plain: schema: $ref: "#/components/schemas/XXX" /testOneParameter: post: operationId: testOneParameter requestBody: content: application/json: schema: $ref: "#/components/schemas/XXX" application/protobuf: schema: $ref: "#/components/schemas/XXX" text/plain: schema: $ref: "#/components/schemas/XXX" x-name: testType1 responses: "200": description: response of 200 content: application/json: schema: $ref: "#/components/schemas/XXX" application/protobuf: schema: $ref: "#/components/schemas/XXX" text/plain: schema: $ref: "#/components/schemas/XXX" components: schemas: XXX: type: object properties: val1: type: integer format: int32 x-java-class: org.apache.servicecomb.swagger.generator.core.pojo.TestType1 testMultiParameterBody: type: object properties: testType1: $ref: "#/components/schemas/XXX" testString: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testResponseHeader: post: operationId: testResponseHeader responses: default: description: response of default headers: h: description: "" schema: type: string description: "" format: "" content: {} "200": description: response of 200 content: application/json: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testSet: post: operationId: testSet requestBody: content: application/json: schema: $ref: '#/components/schemas/testSetBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testSetBody: type: object properties: value: uniqueItems: true type: array items: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testshort: post: operationId: testshort requestBody: content: application/json: schema: $ref: '#/components/schemas/testshortBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testshortBody: type: object properties: value: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testShort: post: operationId: testShort requestBody: content: application/json: schema: $ref: '#/components/schemas/testShortBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testShortBody: type: object properties: value: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testString: post: operationId: testString requestBody: content: application/json: schema: $ref: '#/components/schemas/testStringBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: testStringBody: type: object properties: value: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testCompletableFutureOptional: post: operationId: testCompletableFutureOptional responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /testOptional: post: operationId: testOptional responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: {} ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema version: 1.0.0 servers: - url: /Schema paths: /wrapToBodyWithDesc: post: operationId: wrapToBodyWithDesc requestBody: content: application/json: schema: $ref: '#/components/schemas/wrapToBodyWithDescBody' responses: "200": description: response of 200 content: application/json: {} components: schemas: wrapToBodyWithDescBody: type: object properties: desc: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- swagger: '2.0' info: title: rest test version: 1.0.0 basePath: /springmvc/controller produces: - application/json paths: /sayhello: post: operationId: sayHello parameters: - name: name in: body required: true type: string responses: "200": description: say hello schema: type: string ================================================ FILE: swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- swagger: '2.0' info: title: rest test version: 1.0.0 basePath: /springmvc/controller produces: - application/json paths: /sayhello: post: operationId: sayHello parameters: - name: name in: body required: true schema: type: string responses: "200": description: say hello schema: type: string ================================================ FILE: swagger/swagger-generator/generator-jaxrs/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-generator 3.4.0-SNAPSHOT swagger-generator-jaxrs Java Chassis::Swagger::Generator::Jaxrs org.apache.servicecomb swagger-generator-core org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsOperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.List; import java.util.Map; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.apache.servicecomb.swagger.generator.rest.RestOperationGenerator; import jakarta.ws.rs.BeanParam; public class JaxrsOperationGenerator extends RestOperationGenerator { public JaxrsOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { super(swaggerGenerator, method); } @Override protected void initMethodParameterGenerators(Map> methodAnnotationMap) { super.initMethodParameterGenerators(methodAnnotationMap); parameterGenerators.stream() .filter(pg -> pg.getHttpParameterType() == null) .forEach(pg -> pg.setHttpParameterType(HttpParameterType.BODY)); } @Override protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator, Parameter methodParameter) { return methodParameter.getAnnotation(BeanParam.class) != null; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import jakarta.ws.rs.HttpMethod; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.rest.RestSwaggerGenerator; public class JaxrsSwaggerGenerator extends RestSwaggerGenerator { public JaxrsSwaggerGenerator(Class cls) { super(cls); } @Override protected boolean isSkipMethod(Method method) { if (super.isSkipMethod(method)) { return true; } for (Annotation annotation : method.getAnnotations()) { HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class); if (httpMethod != null) { return false; } } return true; } @SuppressWarnings("unchecked") @Override public T createOperationGenerator(Method method) { return (T) new JaxrsOperationGenerator(this, method); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import jakarta.ws.rs.Path; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory; public class JaxrsSwaggerGeneratorFactory implements SwaggerGeneratorFactory { private static final int ORDER = 2000; @Override public int getOrder() { return ORDER; } @Override public boolean canProcess(Class cls) { return SwaggerUtils.hasAnnotation(cls, Path.class); } @Override public SwaggerGenerator create(Class cls) { return new JaxrsSwaggerGenerator(cls); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.Consumes; public class ConsumesClassAnnotationProcessor extends JaxrsClassAnnotationProcessor { @Override public Type getProcessType() { return Consumes.class; } @Override public void process(SwaggerGenerator swaggerGenerator, Consumes consumes) { if (consumes.value() != null && consumes.value().length > 0) { swaggerGenerator.getSwaggerGeneratorContext().updateConsumes(Arrays.asList(consumes.value())); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.Consumes; public class ConsumesMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { @Override public Type getProcessType() { return Consumes.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Consumes consumes) { if (consumes.value() != null && consumes.value().length > 0) { operationGenerator.getOperationGeneratorContext().updateConsumes(Arrays.asList(consumes.value())); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import jakarta.ws.rs.CookieParam; public class CookieParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { @Override public Type getProcessType() { return CookieParam.class; } @Override public String getParameterName(CookieParam annotation) { if (StringUtils.isEmpty(annotation.value())) { return null; } return annotation.value(); } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, CookieParam annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.COOKIE); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/DeleteMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import jakarta.ws.rs.DELETE; public class DeleteMethodAnnotationProcessor extends GetMethodAnnotationProcessor { @Override public Type getProcessType() { return DELETE.class; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import jakarta.ws.rs.FormParam; public class FormParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { @Override public Type getProcessType() { return FormParam.class; } @Override public String getParameterName(FormParam annotation) { if (StringUtils.isNotEmpty(annotation.value())) { return annotation.value(); } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, FormParam annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.FORM); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/GetMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.GET; import jakarta.ws.rs.HttpMethod; public class GetMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { @Override public Type getProcessType() { return GET.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Annotation annotation) { HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class); operationGenerator.setHttpMethod(httpMethod.value()); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import jakarta.ws.rs.HeaderParam; public class HeaderParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { @Override public Type getProcessType() { return HeaderParam.class; } @Override public String getParameterName(HeaderParam annotation) { if (StringUtils.isNotEmpty(annotation.value())) { return annotation.value(); } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, HeaderParam annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.HEADER); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; /** * A generic class to help classify implementations. */ public abstract class JaxrsClassAnnotationProcessor implements ClassAnnotationProcessor { } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; /** * A generic class to help classify implementations. */ public abstract class JaxrsMethodAnnotationProcessor implements MethodAnnotationProcessor { } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor; /** * A generic class to help classify implementations. */ public abstract class JaxrsParameterAnnotationProcessor implements ParameterAnnotationProcessor { } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PatchMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import jakarta.ws.rs.PATCH; public class PatchMethodAnnotationProcessor extends GetMethodAnnotationProcessor { @Override public Type getProcessType() { return PATCH.class; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.Path; public class PathClassAnnotationProcessor extends JaxrsClassAnnotationProcessor { @Override public Type getProcessType() { return Path.class; } @Override public void process(SwaggerGenerator swaggerGenerator, Path path) { swaggerGenerator.setBasePath(path.value()); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.Path; public class PathMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { @Override public Type getProcessType() { return Path.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Path path) { operationGenerator.setPath(path.value()); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import jakarta.ws.rs.PathParam; public class PathParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { @Override public Type getProcessType() { return PathParam.class; } @Override public String getParameterName(PathParam annotation) { if (StringUtils.isNotEmpty(annotation.value())) { return annotation.value(); } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, PathParam annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.PATH); parameterGenerator.getParameterGeneratorContext().setRequired(true); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PostMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import jakarta.ws.rs.POST; public class PostMethodAnnotationProcessor extends GetMethodAnnotationProcessor { @Override public Type getProcessType() { return POST.class; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.Produces; public class ProducesClassAnnotationProcessor extends JaxrsClassAnnotationProcessor { @Override public Type getProcessType() { return Produces.class; } @Override public void process(SwaggerGenerator swaggerGenerator, Produces produces) { if (produces.value() != null && produces.value().length > 0) { swaggerGenerator.getSwaggerGeneratorContext().updateProduces(Arrays.asList(produces.value())); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import jakarta.ws.rs.Produces; public class ProducesMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { @Override public Type getProcessType() { return Produces.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Produces produces) { if (produces.value() != null && produces.value().length > 0) { operationGenerator.getOperationGeneratorContext().updateProduces(Arrays.asList(produces.value())); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PutMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import jakarta.ws.rs.PUT; public class PutMethodAnnotationProcessor extends GetMethodAnnotationProcessor { @Override public Type getProcessType() { return PUT.class; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamParameterAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import jakarta.ws.rs.QueryParam; public class QueryParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { @Override public Type getProcessType() { return QueryParam.class; } @Override public String getParameterName(QueryParam annotation) { if (StringUtils.isNotEmpty(annotation.value())) { return annotation.value(); } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, QueryParam annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.QUERY); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/response/JaxrsResponseProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.processor.response; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; public class JaxrsResponseProcessor extends DefaultResponseTypeProcessor { @Override public Class getProcessType() { return Response.class; } @Override public Type extractResponseType(Type genericResponseType) { return null; } @Override public Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType) { ApiResponses responses = operationGenerator.getOperation().getResponses(); if (responses != null) { ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); if (response != null && response.getContent() != null) { if (response.getContent().get(MediaType.TEXT_PLAIN) != null) { return String.class; } } } throw new IllegalStateException("Use ApiOperation or ApiResponses to declare response type"); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PathClassAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesClassAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ProducesClassAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ProducesMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PathMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.GetMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PostMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PutMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.DeleteMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PatchMethodAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PathParamParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.QueryParamParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.HeaderParamParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.CookieParamParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.FormParamParameterAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.jaxrs.processor.response.JaxrsResponseProcessor ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorFactory ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassAnnotation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import jakarta.servlet.http.Part; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; @Path("/class") @Consumes(value = {MediaType.APPLICATION_JSON}) @Produces(value = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) @SuppressWarnings("unused") public class ClassAnnotation { @Path(value = "testBean") @POST public User testBean(User user) { return null; } @Path(value = "testString") @POST public String testString(String user) { return null; } // This case should cause error. Must implicitly specify consumes. // For JAX RS, @FormParam can be url-encoded-form or multipart. @Path(value = "testFormWrong") @POST public String testFormWrong(@FormParam("param") int param) { return null; } // This case should cause error. Must implicitly specify consumes // For JAX RS, @FormParam can be url-encoded-form or multipart. @Path(value = "testUploadWrong") @POST public String testUploadWrong(@FormParam("part") Part part) { return null; } @Path(value = "testForm") @POST @Consumes(value = MediaType.APPLICATION_FORM_URLENCODED) public String testForm(@FormParam("param") int param) { return null; } @Path(value = "testUpload") @POST @Consumes(value = MediaType.MULTIPART_FORM_DATA) public String testUpload(@FormParam("part") Part part) { return null; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("") public class ClassMethodNoPath { @GET public void p1() { } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import java.util.List; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; import org.apache.servicecomb.swagger.generator.jaxrs.model.AggregatedParam; import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamComplexField; import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamComplexSetter; import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamDefaultBody; import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamWithJsonIgnoredTagged; import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamWithPart; import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatus; import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatusBeanParam; import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatusModel; import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatus; import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusBeanParam; import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.CookieParam; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.PATCH; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Response; @Path(value = "Echo") public class Echo { @PATCH public void patch() { } @POST @ApiResponse(content = { @Content(schema = @Schema(implementation = Integer.class))}, responseCode = "200", description = "") @Path("response") public Response response() { return null; } @POST @Operation(summary = "") @Path("emptyPath") public void emptyPath() { } @Path(value = "echo/{targetName}") @POST public String echo(User srcUser, @HeaderParam(value = "header") String header, @PathParam(value = "targetName") String targetName, @QueryParam(value = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @Path(value = "cookie") @POST public String cookie(@CookieParam(value = "cookie") String cookie) { return String.format("%s", cookie); } @Path(value = "form") @POST public String form(@FormParam(value = "form") String form) { return String.format("%s", form); } @Path(value = "query") @GET public String query(@QueryParam(value = "query") String query) { return String.format("%s", query); } @Path(value = "queryComplex") @GET public String queryComplex(@QueryParam(value = "queries") List queries) { return String.format("%s", queries); } @Operation(summary = "") public void ignoredNonRestful() { } @Path(value = "testRawJson") @POST public void rawJsonStringMethod(@RawJsonRequestBody String jsonInput) { } @Path(value = "enumBody") @POST public void enumBody(Color color) { } @Path("aggregatedParam") @POST public void aggregatedParam(@BeanParam AggregatedParam aggregatedParam) { } @Path("beanParamWithPart") @POST public void beanParamWithPart(@BeanParam BeanParamWithPart beanParamWithPart) { } @Path("beanParamComplexField") @POST public void beanParamComplexField(@BeanParam BeanParamComplexField beanParamComplexField) { } @Path("beanParamComplexSetter") @POST public void beanParamComplexSetter(@BeanParam BeanParamComplexSetter beanParamComplexSetter) { } @Path("beanParamDefaultBody") @POST public void beanParamDefaultBody(@BeanParam BeanParamDefaultBody beanParamDefaultBody) { } @Path("beanParamWithJsonIgnoredTaggedBody") @POST public void beanParamWithJsonIgnoredTagged(@BeanParam BeanParamWithJsonIgnoredTagged beanParamWithJsonIgnoredTagged) { } @Path("nestedListString") @POST public List> nestedListString(List> param) { return param; } @Path("/dynamicStatusEnum") @POST public DynamicStatus dynamicStatusEnum(@BeanParam DynamicStatusBeanParam statusBeanParam, @QueryParam("status") @Parameter(description = "dynamic desc direct") DynamicStatus status, DynamicStatusModel model) { return null; } @Path("/jdkStatusEnum") @POST public JdkStatus jdkStatusEnum(@BeanParam JdkStatusBeanParam statusBeanParam, @QueryParam("status") @Parameter(description = "jdk desc direct") JdkStatus status, JdkStatusModel model) { return null; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/FullSwaggerService.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import java.io.File; import java.io.IOException; import java.util.List; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.enums.ParameterStyle; import jakarta.servlet.http.Part; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; @Path(value = "/FullSwaggerService") public class FullSwaggerService { @Path("/fileUpload") @POST @Produces(MediaType.TEXT_PLAIN) public String fileUpload(@FormParam("file1") Part file1, @FormParam("file2") Part file2) throws IOException { return null; } @Path("/queryListMULTI") @GET public String queryListMULTI( @Parameter(name = "queryList", in = ParameterIn.QUERY, style = ParameterStyle.FORM, explode = Explode.TRUE) @QueryParam("queryList") List queryList) { return queryList == null ? "null" : queryList.size() + ":" + queryList; } @Path("/defaultValue") @GET public String defaultValue(@QueryParam("e") int e, @DefaultValue("20") @QueryParam("a") int a, @DefaultValue("bobo") @QueryParam("b") String b, @DefaultValue("40") @QueryParam("c") Integer c, @Min(value = 20) @Max(value = 30) @QueryParam("d") int d) { return "Hello " + a + b + c + d + e; } @Path("/textPlain") @GET @Produces(MediaType.TEXT_PLAIN) public String textPlain() { return null; } @Path("/fileDownload") @GET public File fileDownload() { return null; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/abc") public class MultiDefaultPath { @GET public void p1() { } @GET public void p2() { } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestClassAnnotation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.junit.jupiter.api.Test; public class TestClassAnnotation { @Test public void test_generate_swagger_correct() { UnitTestSwaggerUtils.testSwagger("schemas/ClassAnnotation.yaml", ClassAnnotation.class, "testBean", "testString", "testForm", "testUpload"); } @Test public void test_form_wrong() { UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + "method=ClassAnnotation:testFormWrong, cause=Consumes not provided for FORM parameter, " + "or is empty by annotations rule.", ClassAnnotation.class, "testFormWrong"); } @Test public void test_upload_wrong() { UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + "method=ClassAnnotation:testUploadWrong, " + "cause=Part type must declare consumes multipart/form-data", ClassAnnotation.class, "testUploadWrong"); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.apache.servicecomb.swagger.generator.jaxrs.model.ConsumesAndProduces; import org.junit.jupiter.api.Test; public class TestJaxrs { @Test public void testMultiDefaultPath() { UnitTestSwaggerUtils.testException( "Duplicate operation path detected. method=org.apache.servicecomb.swagger.generator.jaxrs.MultiDefaultPath:p2.", MultiDefaultPath.class); } @Test public void testEcho() { UnitTestSwaggerUtils.testSwagger("schemas/echo.yaml", Echo.class); } @Test public void testClassMethodNoPath() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, " + "method=ClassMethodNoPath:p1, cause=Path must not both be empty in class and method", ClassMethodNoPath.class); } @Test public void testFullSwaggerService() { UnitTestSwaggerUtils.testSwagger("schemas/FullSwaggerService.yaml", FullSwaggerService.class); } @Test public void consumesAndProduces_exception_testSingleMediaType() { UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + "method=ConsumesAndProduces:testSingleMediaType, " + "cause=Not support media type application/xml", ConsumesAndProduces.class, "testSingleMediaType"); } @Test public void consumesAndProduces_exception_testMultipleMediaType() { UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + "method=ConsumesAndProduces:testMultipleMediaType, " + "cause=Not support media type application/xml", ConsumesAndProduces.class, "testMultipleMediaType"); } @Test public void consumesAndProduces_exception_testBlankMediaType() { UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + "method=ConsumesAndProduces:testBlankMediaType, cause=Not support media type ", ConsumesAndProduces.class, "testBlankMediaType"); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/AggregatedParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; import java.util.List; import jakarta.ws.rs.CookieParam; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.QueryParam; public class AggregatedParam { @DefaultValue("pa") @PathParam("path0") private String strVal; @QueryParam("query1") private int intVal; private long longVal; private long cookieVal; @HeaderParam("header2") private String headerVal; @QueryParam("query-array") private String[] queryArray; @QueryParam("query-list") private List queryList; public String getStrVal() { return strVal; } public void setStrVal(String strVal) { this.strVal = strVal; } public int getIntVal() { return intVal; } public void setIntVal(int intVal) { this.intVal = intVal; } public long getLongVal() { return longVal; } @DefaultValue("12") @FormParam("form3") public void setLongVal(long longVal) { this.longVal = longVal; } public long getCookieVal() { return cookieVal; } @CookieParam("cookie4") public void setCookieVal(long cookieVal) { this.cookieVal = cookieVal; } public String getHeaderVal() { return headerVal; } public void setHeaderVal(String headerVal) { this.headerVal = headerVal; } public String[] getQueryArray() { return queryArray; } public AggregatedParam setQueryArray(String[] queryArray) { this.queryArray = queryArray; return this; } public List getQueryList() { return queryList; } public AggregatedParam setQueryList(List queryList) { this.queryList = queryList; return this; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexField.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; import jakarta.ws.rs.QueryParam; public class BeanParamComplexField { @QueryParam("q") private AggregatedParam complex; public AggregatedParam getComplex() { return complex; } public void setComplex(AggregatedParam complex) { this.complex = complex; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexSetter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; import jakarta.ws.rs.HeaderParam; public class BeanParamComplexSetter { private AggregatedParam complex; public AggregatedParam getComplex() { return complex; } @HeaderParam("h") public void setComplex(AggregatedParam complex) { this.complex = complex; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamDefaultBody.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; public class BeanParamDefaultBody { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamInvalidDefaultBody.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; public class BeanParamInvalidDefaultBody { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithJsonIgnoredTagged.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; import jakarta.ws.rs.QueryParam; import com.fasterxml.jackson.annotation.JsonIgnore; public class BeanParamWithJsonIgnoredTagged { @QueryParam("name") private String name; @JsonIgnore private AggregatedParam ignored; public String getName() { return name; } public void setName(String name) { this.name = name; } public AggregatedParam getIgnored() { return ignored; } public void setIgnored( AggregatedParam ignored) { this.ignored = ignored; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithPart.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; import jakarta.servlet.http.Part; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.QueryParam; public class BeanParamWithPart { @QueryParam("queryStr") private boolean queryStr; @FormParam("up0") private Part up0; private Part up1; public boolean isQueryStr() { return queryStr; } public void setQueryStr(boolean queryStr) { this.queryStr = queryStr; } public Part getUp0() { return up0; } public void setUp0(Part up0) { this.up0 = up0; } public Part getUp1() { return up1; } @FormParam("up1") public void setUp1(Part up1) { this.up1 = up1; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/ConsumesAndProduces.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; public class ConsumesAndProduces { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_XML) public String testSingleMediaType(String input) { return input; } @Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) @Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML}) public String testMultipleMediaType(String input) { return input; } @Consumes("") @Produces("") public String testBlankMediaType(String input) { return input; } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model.enums; import org.apache.servicecomb.foundation.common.base.DynamicEnum; import org.apache.servicecomb.foundation.common.base.DynamicEnumCache; import com.fasterxml.jackson.annotation.JsonCreator; import io.swagger.v3.oas.annotations.Parameter; public class DynamicStatus extends DynamicEnum { @Parameter(description = "dynamic bad request") public static final DynamicStatus BAD_REQUEST = new DynamicStatus(400); @Parameter(description = "dynamic not found") public static final DynamicStatus NOT_FOUND = new DynamicStatus(404); private static final DynamicEnumCache CACHE = new DynamicEnumCache<>(DynamicStatus.class); public DynamicStatus(Integer value) { super(value); } @JsonCreator public static DynamicStatus fromValue(int value) { return CACHE.fromValue(value); } } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusBeanParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model.enums; import io.swagger.v3.oas.annotations.Parameter; import jakarta.ws.rs.QueryParam; public class DynamicStatusBeanParam { @Parameter(description = "dynamic desc aggr") @QueryParam("status-aggr") public DynamicStatus queryStatus; } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model.enums; import io.swagger.v3.oas.annotations.Parameter; public class DynamicStatusModel { @Parameter(description = "dynamic status model") public DynamicStatus status; } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model.enums; import io.swagger.v3.oas.annotations.Parameter; public enum JdkStatus { @Parameter(description = "jdk bad request") BAD_REQUEST, @Parameter(description = "jdk not found") NOT_FOUND } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusBeanParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model.enums; import io.swagger.v3.oas.annotations.Parameter; import jakarta.ws.rs.QueryParam; public class JdkStatusBeanParam { @Parameter(description = "jdk desc aggr") @QueryParam("status-aggr") public JdkStatus queryStatus; } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.jaxrs.model.enums; import io.swagger.v3.oas.annotations.Parameter; public class JdkStatusModel { @Parameter(description = "jdk status model") public JdkStatus status; } ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.ClassAnnotation version: 1.0.0 servers: - url: /class paths: /testBean: post: operationId: testBean requestBody: content: application/json: schema: $ref: "#/components/schemas/User" x-name: user responses: "200": description: response of 200 content: application/json: schema: $ref: "#/components/schemas/User" text/plain: schema: $ref: "#/components/schemas/User" /testForm: post: operationId: testForm requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: param: type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: string text/plain: schema: type: string /testString: post: operationId: testString requestBody: content: application/json: schema: type: string x-name: user responses: "200": description: response of 200 content: application/json: schema: type: string text/plain: schema: type: string /testUpload: post: operationId: testUpload requestBody: content: multipart/form-data: schema: type: object properties: part: type: string format: binary responses: "200": description: response of 200 content: application/json: schema: type: string text/plain: schema: type: string components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.FullSwaggerService version: 1.0.0 servers: - url: /FullSwaggerService paths: /defaultValue: get: operationId: defaultValue parameters: - name: e in: query schema: type: integer format: int32 - name: a in: query schema: type: integer format: int32 default: 20 - name: b in: query schema: type: string default: bobo - name: c in: query schema: type: integer format: int32 default: 40 - name: d in: query schema: maximum: 30 minimum: 20 type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /fileDownload: get: operationId: fileDownload responses: "200": description: response of 200 content: '*/*': schema: type: string format: binary /fileUpload: post: operationId: fileUpload requestBody: content: multipart/form-data: schema: type: object properties: file1: type: string format: binary file2: type: string format: binary responses: "200": description: response of 200 content: text/plain: schema: type: string /queryListMULTI: get: operationId: queryListMULTI parameters: - name: queryList in: query required: false style: form explode: true schema: type: array items: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /textPlain: get: operationId: textPlain responses: "200": description: response of 200 content: text/plain: schema: type: string components: {} ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/aggregatedParam.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /aggregatedParam: post: operationId: "aggregatedParam" parameters: - name: "path0" in: "path" required: true type: "string" default: "pa" - name: "query1" in: "query" required: false type: "integer" default: 0 format: "int32" - name: "form3" in: "formData" required: false type: "integer" default: 12 format: "int64" - name: "cookie4" in: "cookie" required: false type: "integer" default: 0 format: "int64" - name: "header2" in: "header" required: false type: "string" - name: "query-array" in: "query" required: false type: "array" items: type: "string" collectionFormat: "multi" - name: "query-list" in: "query" required: false type: "array" items: type: "string" collectionFormat: "multi" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamDefaultBody.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /beanParamDefaultBody: post: operationId: "beanParamDefaultBody" parameters: - in: "body" name: "name" required: false schema: type: "string" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithJsonIgnoredTagged.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /beanParamWithJsonIgnoredTaggedBody: post: operationId: "beanParamWithJsonIgnoredTagged" parameters: - name: "name" in: "query" required: false type: "string" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithPart.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /beanParamWithPart: post: operationId: "beanParamWithPart" consumes: - "multipart/form-data" parameters: - name: "queryStr" in: "query" required: false type: "boolean" default: false - name: "up0" in: "formData" required: false type: "file" - name: "up1" in: "formData" required: false type: "file" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.model.ConsumesAndProduces version: 1.0.0 servers: - url: /ConsumesAndProduces paths: /testBlankMediaType: post: operationId: testBlankMediaType requestBody: content: application/json: schema: $ref: '#/components/schemas/testBlankMediaTypeBody' x-name: testBlankMediaTypeBody responses: "200": description: response of 200 content: application/json: schema: type: string /testMultipleMediaType: post: operationId: testMultipleMediaType requestBody: content: application/json: schema: $ref: '#/components/schemas/testMultipleMediaTypeBody' x-name: testMultipleMediaTypeBody responses: "200": description: response of 200 content: application/json: schema: type: string /testSingleMediaType: post: operationId: testSingleMediaType requestBody: content: application/json: schema: $ref: '#/components/schemas/testSingleMediaTypeBody' x-name: testSingleMediaTypeBody responses: "200": description: response of 200 content: application/json: schema: type: string components: schemas: testBlankMediaTypeBody: type: object properties: input: type: string testMultipleMediaTypeBody: type: object properties: input: type: string testSingleMediaTypeBody: type: object properties: input: type: string ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/cookie.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /cookie: post: operationId: "cookie" parameters: - name: "cookie" in: "cookie" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo version: 1.0.0 servers: - url: Echo paths: /dynamicStatusEnum: post: operationId: dynamicStatusEnum parameters: - name: status-aggr in: query description: | dynamic desc aggr - 400: dynamic bad request - 404: dynamic not found - name: status in: query description: | dynamic desc direct - 400: dynamic bad request - 404: dynamic not found requestBody: content: application/json: schema: $ref: '#/components/schemas/DynamicStatusModel' responses: "200": description: response of 200 content: application/json: schema: type: integer description: | - 400: dynamic bad request - 404: dynamic not found format: int32 components: schemas: DynamicStatusModel: type: object properties: status: type: integer format: int32 ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo version: 1.0.0 servers: - url: Echo paths: /aggregatedParam: post: operationId: aggregatedParam parameters: - name: path0 in: path required: true schema: type: string default: pa - name: query1 in: query schema: type: integer format: int32 - name: cookie4 in: cookie schema: type: integer format: int64 - name: header2 in: header schema: type: string - name: query-array in: query schema: type: array items: type: string - name: query-list in: query schema: type: array items: type: string requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: form3: type: integer format: int64 responses: "200": description: response of 200 /beanParamComplexField: post: operationId: beanParamComplexField parameters: - name: q in: query schema: $ref: "#/components/schemas/AggregatedParam" responses: "200": description: response of 200 /beanParamComplexSetter: post: operationId: beanParamComplexSetter parameters: - name: h in: header schema: $ref: "#/components/schemas/AggregatedParam" responses: "200": description: response of 200 /beanParamDefaultBody: post: operationId: beanParamDefaultBody requestBody: content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string x-name: name responses: "200": description: response of 200 /beanParamWithJsonIgnoredTaggedBody: post: operationId: beanParamWithJsonIgnoredTagged parameters: - name: name in: query schema: type: string responses: "200": description: response of 200 /beanParamWithPart: post: operationId: beanParamWithPart parameters: - name: queryStr in: query schema: type: boolean requestBody: content: multipart/form-data: schema: type: object properties: up0: type: string format: binary up1: type: string format: binary responses: "200": description: response of 200 /cookie: post: operationId: cookie parameters: - name: cookie in: cookie schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /dynamicStatusEnum: post: operationId: dynamicStatusEnum parameters: - name: status-aggr in: query description: dynamic desc aggr required: false schema: type: integer format: int32 - name: status in: query description: dynamic desc direct required: false schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: "#/components/schemas/DynamicStatusModel" application/protobuf: schema: $ref: "#/components/schemas/DynamicStatusModel" text/plain: schema: $ref: "#/components/schemas/DynamicStatusModel" x-name: model responses: "200": description: response of 200 content: application/json: schema: type: integer format: int32 application/protobuf: schema: type: integer format: int32 text/plain: schema: type: integer format: int32 /echo/{targetName}: post: operationId: echo parameters: - name: header in: header schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" application/protobuf: schema: $ref: "#/components/schemas/User" text/plain: schema: $ref: "#/components/schemas/User" x-name: srcUser responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /emptyPath: post: operationId: emptyPath responses: "200": description: response of 200 /enumBody: post: operationId: enumBody requestBody: content: application/json: schema: type: string enum: - RED - YELLOW - BLUE application/protobuf: schema: type: string enum: - RED - YELLOW - BLUE text/plain: schema: type: string enum: - RED - YELLOW - BLUE x-name: color responses: "200": description: response of 200 /form: post: operationId: form requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: form: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /jdkStatusEnum: post: operationId: jdkStatusEnum parameters: - name: status-aggr in: query description: jdk desc aggr required: false schema: type: string enum: - BAD_REQUEST - NOT_FOUND - name: status in: query description: jdk desc direct required: false schema: type: string enum: - BAD_REQUEST - NOT_FOUND requestBody: content: application/json: schema: $ref: "#/components/schemas/JdkStatusModel" application/protobuf: schema: $ref: "#/components/schemas/JdkStatusModel" text/plain: schema: $ref: "#/components/schemas/JdkStatusModel" x-name: model responses: "200": description: response of 200 content: application/json: schema: type: string enum: - BAD_REQUEST - NOT_FOUND application/protobuf: schema: type: string enum: - BAD_REQUEST - NOT_FOUND text/plain: schema: type: string enum: - BAD_REQUEST - NOT_FOUND /nestedListString: post: operationId: nestedListString requestBody: content: application/json: schema: type: array items: type: array items: type: string application/protobuf: schema: type: array items: type: array items: type: string text/plain: schema: type: array items: type: array items: type: string x-name: param responses: "200": description: response of 200 content: application/json: schema: type: array items: type: array items: type: string application/protobuf: schema: type: array items: type: array items: type: string text/plain: schema: type: array items: type: array items: type: string /: patch: operationId: patch responses: "200": description: response of 200 /query: get: operationId: query parameters: - name: query in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /queryComplex: get: operationId: queryComplex parameters: - name: queries in: query schema: type: array items: $ref: "#/components/schemas/User" responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testRawJson: post: operationId: rawJsonStringMethod requestBody: content: application/json: schema: type: string text/plain: schema: type: string required: true x-raw-json: true x-name: jsonInput responses: "200": description: response of 200 /response: post: operationId: response responses: "200": description: response of 200 content: application/json: schema: type: integer format: int32 application/protobuf: schema: type: integer format: int32 text/plain: schema: type: integer format: int32 components: schemas: AggregatedParam: type: object properties: strVal: type: string intVal: type: integer format: int32 longVal: type: integer format: int64 cookieVal: type: integer format: int64 headerVal: type: string queryArray: type: array items: type: string queryList: type: array items: type: string x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.AggregatedParam DynamicStatusModel: type: object properties: status: type: integer format: int32 x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatusModel User: type: object properties: name: type: string friends: type: array items: $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User JdkStatusModel: type: object properties: status: type: string enum: - BAD_REQUEST - NOT_FOUND x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyContract.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyPath.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /: post: operationId: "emptyPath" parameters: [] responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/enumBody.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /enumBody: post: operationId: "enumBody" parameters: - in: "body" name: "color" required: false schema: type: "string" enum: - "RED" - "YELLOW" - "BLUE" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/form.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /form: post: operationId: "form" parameters: - name: "form" in: "formData" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/jdkStatusEnum.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /jdkStatusEnum: post: operationId: "jdkStatusEnum" parameters: - name: "status-aggr" in: "query" description: "jdk desc aggr\n- BAD_REQUEST: jdk bad request\n- NOT_FOUND:\ \ jdk not found\n" required: false type: "string" enum: - "BAD_REQUEST" - "NOT_FOUND" - name: "status" in: "query" description: "jdk desc direct\n- BAD_REQUEST: jdk bad request\n- NOT_FOUND:\ \ jdk not found\n" required: false type: "string" enum: - "BAD_REQUEST" - "NOT_FOUND" - in: "body" name: "model" required: false schema: $ref: "#/definitions/JdkStatusModel" responses: "200": description: "response of 200" schema: type: "string" description: "- BAD_REQUEST: jdk bad request\n- NOT_FOUND: jdk not found\n" enum: - "BAD_REQUEST" - "NOT_FOUND" x-java-class: "org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatus" definitions: JdkStatusModel: type: "object" properties: status: type: "string" description: "jdk status model\n- BAD_REQUEST: jdk bad request\n- NOT_FOUND:\ \ jdk not found\n" enum: - "BAD_REQUEST" - "NOT_FOUND" x-java-class: "org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo version: 1.0.0 servers: - url: Echo paths: /nestedListString: post: operationId: nestedListString requestBody: content: application/json: schema: type: array items: type: array items: type: string responses: "200": description: response of 200 content: application/json: schema: type: array items: type: array items: type: string components: schemas: {} ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/patch.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /: patch: operationId: "patch" parameters: [] responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/query.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /query: get: operationId: "query" parameters: - name: "query" in: "query" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/rawJsonStringMethod.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /testRawJson: post: operationId: "rawJsonStringMethod" parameters: - in: "body" name: "jsonInput" required: true schema: type: "string" x-raw-json: true responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/response.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /: post: operationId: "response" parameters: [] responses: "200": description: "response of 200" schema: type: "integer" format: "int32" ================================================ FILE: swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/responseText.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "application/json" produces: - "application/json" paths: /: get: operationId: "responseText" produces: - "text/plain" parameters: [] responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-spring-data/pom.xml ================================================ org.apache.servicecomb swagger-generator 3.4.0-SNAPSHOT 4.0.0 swagger-generator-spring-data Java Chassis::Swagger::Generator::Spring data org.apache.servicecomb swagger-generator-core org.springframework.data spring-data-commons ================================================ FILE: swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataConcreteTypeRegister.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springdata; import java.lang.reflect.Type; import java.util.Set; import org.apache.servicecomb.swagger.extend.ConcreteTypeRegister; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public class SpringDataConcreteTypeRegister implements ConcreteTypeRegister { @Override public void register(Set types) { types.add(Page.class); types.add(Pageable.class); } } ================================================ FILE: swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataModule.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springdata; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.Converter; public class SpringDataModule extends SimpleModule implements SPIOrder { private static final long serialVersionUID = 1L; @JsonDeserialize(as = PageImpl.class) @JsonPropertyOrder(alphabetic = true) public static class PageMixin { @JsonCreator public PageMixin(@JsonProperty(value = "content") List content, @JsonProperty("pageable") Pageable pageable, @JsonProperty("total") long total) { } } @JsonDeserialize(as = PageRequest.class) @JsonPropertyOrder(alphabetic = true) public static class PageableMixin { @JsonCreator public PageableMixin(@JsonProperty(value = "pageNumber") int page, @JsonProperty("pageSize") int size, @JsonProperty(value = "sort") Sort sort) { } } public static class SortConverter implements Converter { @Override public Sort convert(SortMixin value) { return Sort.by(value.getProperties()); } @Override public JavaType getInputType(TypeFactory typeFactory) { return typeFactory.constructType(SortMixin.class); } @Override public JavaType getOutputType(TypeFactory typeFactory) { return typeFactory.constructType(Sort.class); } } public static class SortMixinConverter implements Converter { @Override public SortMixin convert(Sort value) { List properties = new ArrayList<>(); for (Order order : value) { properties.add(order.getProperty()); } SortMixin result = new SortMixin(); result.setProperties(properties.toArray(new String[0])); return result; } @Override public JavaType getInputType(TypeFactory typeFactory) { return typeFactory.constructType(Sort.class); } @Override public JavaType getOutputType(TypeFactory typeFactory) { return typeFactory.constructType(SortMixin.class); } } @JsonPropertyOrder(alphabetic = true) @JsonDeserialize(converter = SortConverter.class) @JsonSerialize(converter = SortMixinConverter.class) public static class SortMixin { private String[] properties; @JsonCreator public SortMixin() { } public void setProperties(String[] properties) { this.properties = properties; } public String[] getProperties() { return this.properties; } } public SpringDataModule() { super("springData"); setMixInAnnotation(Page.class, PageMixin.class); setMixInAnnotation(Pageable.class, PageableMixin.class); setMixInAnnotation(Sort.class, SortMixin.class); setMixInAnnotation(PageImpl.class, PageMixin.class); setMixInAnnotation(PageRequest.class, PageableMixin.class); } @Override public Object getTypeId() { return getModuleName(); } @Override public int getOrder() { return Short.MAX_VALUE; } } ================================================ FILE: swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springdata.SpringDataModule ================================================ FILE: swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springdata.SpringDataConcreteTypeRegister ================================================ FILE: swagger/swagger-generator/generator-spring-data/src/test/java/org/apache/servicecomb/swagger/generator/springdata/TestPageResponseTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springdata; import java.io.IOException; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import io.swagger.v3.core.util.Json; public class TestPageResponseTypeProcessor { interface PageSchema { Page test(Page page); } @Test public void swagger() { UnitTestSwaggerUtils.testSwagger("pageSchema.yaml", PageSchema.class); } @Test public void deserialize() throws IOException { Json.mapper().registerModule(new SpringDataModule()); Sort sort = Sort.by(Direction.ASC, "name"); Pageable pageable = PageRequest.of(1, 10, sort); Page page = new PageImpl<>(Arrays.asList("c1", "c2"), pageable, 2); String json = Json.mapper().writeValueAsString(page); Assertions.assertEquals( "{\"content\":[\"c1\",\"c2\"],\"pageable\":{\"pageNumber\":1,\"pageSize\":10,\"sort\":{\"properties\":[\"name\"]},\"offset\":10,\"paged\":true,\"unpaged\":false},\"empty\":false,\"first\":false,\"last\":true,\"number\":1,\"numberOfElements\":2,\"size\":10,\"sort\":{\"properties\":[\"name\"]},\"totalElements\":12,\"totalPages\":2}", json); Page page2 = Json.mapper().readValue(json, Page.class); Assertions.assertEquals(json, Json.mapper().writeValueAsString(page2)); } } ================================================ FILE: swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springdata.TestPageResponseTypeProcessor$PageSchema version: 1.0.0 servers: - url: /PageSchema paths: /test: post: operationId: test requestBody: content: application/json: schema: $ref: "#/components/schemas/PageString" application/protobuf: schema: $ref: "#/components/schemas/PageString" text/plain: schema: $ref: "#/components/schemas/PageString" x-name: page responses: "200": description: response of 200 content: application/json: schema: $ref: "#/components/schemas/PageString" application/protobuf: schema: $ref: "#/components/schemas/PageString" text/plain: schema: $ref: "#/components/schemas/PageString" components: schemas: PageString: type: object properties: content: type: array items: type: string empty: type: boolean first: type: boolean last: type: boolean number: type: integer format: int32 numberOfElements: type: integer format: int32 pageable: $ref: "#/components/schemas/Pageable" size: type: integer format: int32 sort: $ref: "#/components/schemas/Sort" totalElements: type: integer format: int64 totalPages: type: integer format: int32 x-java-class: org.springframework.data.domain.Page Pageable: type: object properties: offset: type: integer format: int64 pageNumber: type: integer format: int32 pageSize: type: integer format: int32 paged: type: boolean sort: $ref: "#/components/schemas/Sort" unpaged: type: boolean x-java-class: org.springframework.data.domain.Pageable Sort: type: object properties: empty: type: boolean sorted: type: boolean unsorted: type: boolean x-java-class: org.springframework.data.domain.Sort ================================================ FILE: swagger/swagger-generator/generator-springmvc/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-generator 3.4.0-SNAPSHOT swagger-generator-springmvc Java Chassis::Swagger::Generator::Spring MVC org.springframework spring-webmvc org.apache.servicecomb swagger-generator-core org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcOperationGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.List; import java.util.Map; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.apache.servicecomb.swagger.generator.rest.RestOperationGenerator; import org.springframework.web.bind.annotation.RequestBody; public class SpringmvcOperationGenerator extends RestOperationGenerator { public SpringmvcOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { super(swaggerGenerator, method); } @Override protected void initMethodParameterGenerators(Map> methodAnnotationMap) { super.initMethodParameterGenerators(methodAnnotationMap); parameterGenerators.stream() .filter(pg -> pg.getHttpParameterType() == null) .forEach(pg -> pg.setHttpParameterType(HttpParameterType.QUERY)); } @Override protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator, Parameter methodParameter) { return !isRequestBody(parameterGenerator) && SwaggerUtils.isBean(methodParameter.getParameterizedType()); } private boolean isRequestBody(ParameterGenerator parameterGenerator) { for (Annotation annotation : parameterGenerator.getAnnotations()) { if (annotation.annotationType() == RequestBody.class) { return true; } } return false; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGenerator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import java.lang.reflect.Method; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.rest.RestSwaggerGenerator; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; public class SpringmvcSwaggerGenerator extends RestSwaggerGenerator { public SpringmvcSwaggerGenerator(Class cls) { super(cls); } @Override protected boolean isSkipMethod(Method method) { if (super.isSkipMethod(method)) { return true; } return method.getAnnotation(RequestMapping.class) == null && method.getAnnotation(GetMapping.class) == null && method.getAnnotation(PutMapping.class) == null && method.getAnnotation(PostMapping.class) == null && method.getAnnotation(PatchMapping.class) == null && method.getAnnotation(DeleteMapping.class) == null; } @SuppressWarnings("unchecked") @Override public T createOperationGenerator(Method method) { return (T) new SpringmvcOperationGenerator(this, method); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; public class SpringmvcSwaggerGeneratorFactory implements SwaggerGeneratorFactory { private static final int ORDER = 1000; @Override public int getOrder() { return ORDER; } @Override public boolean canProcess(Class cls) { return SwaggerUtils.hasAnnotation(cls, RequestMapping.class) || SwaggerUtils .hasAnnotation(cls, RestController.class); } @Override public SwaggerGenerator create(Class cls) { return new SpringmvcSwaggerGenerator(cls); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/AbstractHttpMethodMappingAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.springframework.web.bind.annotation.RequestMethod; abstract class AbstractHttpMethodMappingAnnotationProcessor implements MethodAnnotationProcessor { protected void doProcess(OperationGenerator operationGenerator, String[] paths, String[] pathValues, RequestMethod requestMethod, String[] consumes, String[] produces) { // paths same to pathValues this.processPath(operationGenerator, paths); this.processPath(operationGenerator, pathValues); if (requestMethod != null) { operationGenerator.setHttpMethod(requestMethod.name()); } if (consumes.length > 0) { operationGenerator.getOperationGeneratorContext().updateConsumes(Arrays.asList(consumes)); } if (produces.length > 0) { operationGenerator.getOperationGeneratorContext().updateProduces(Arrays.asList(produces)); } } protected void processPath(OperationGenerator operationGenerator, String[] paths) { if (null == paths || paths.length == 0) { return; } // swagger仅支持配一个path,否则将会出现重复的operationId if (paths.length > 1) { throw new IllegalStateException("not allowed multi path."); } operationGenerator.setPath(paths[0]); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.ValueConstants; public class CookieValueAnnotationProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return CookieValue.class; } @Override public String getParameterName(CookieValue annotation) { String value = annotation.value(); if (value.isEmpty()) { value = annotation.name(); } if (StringUtils.isNotEmpty(value)) { return value; } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, CookieValue annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.COOKIE); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); if (!ValueConstants.DEFAULT_NONE.equals(annotation.defaultValue())) { parameterGenerator.getParameterGeneratorContext() .setDefaultValue(annotation.defaultValue()); // if default value is set, must be required false. parameterGenerator.getParameterGeneratorContext().setRequired(false); } } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/DeleteMappingMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestMethod; public class DeleteMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { @Override public Type getProcessType() { return DeleteMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, DeleteMapping deleteMapping) { doProcess(operationGenerator, deleteMapping.path(), deleteMapping.value(), RequestMethod.DELETE, deleteMapping.consumes(), deleteMapping.produces()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/GetMappingMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMethod; public class GetMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { @Override public Type getProcessType() { return GetMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, GetMapping getMapping) { doProcess(operationGenerator, getMapping.path(), getMapping.value(), RequestMethod.GET, getMapping.consumes(), getMapping.produces()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PatchMappingMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestMethod; public class PatchMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { @Override public Type getProcessType() { return PatchMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, PatchMapping patchMapping) { doProcess(operationGenerator, patchMapping.path(), patchMapping.value(), RequestMethod.PATCH, patchMapping.consumes(), patchMapping.produces()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.PathVariable; public class PathVariableAnnotationProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return PathVariable.class; } @Override public String getParameterName(PathVariable annotation) { String value = annotation.value(); if (value.isEmpty()) { value = annotation.name(); } if (StringUtils.isNotEmpty(value)) { return value; } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, PathVariable annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.PATH); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PostMappingMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMethod; public class PostMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { @Override public Type getProcessType() { return PostMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, PostMapping postMapping) { doProcess(operationGenerator, postMapping.path(), postMapping.value(), RequestMethod.POST, postMapping.consumes(), postMapping.produces()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PutMappingMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMethod; public class PutMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { @Override public Type getProcessType() { return PutMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, PutMapping putMapping) { doProcess(operationGenerator, putMapping.path(), putMapping.value(), RequestMethod.PUT, putMapping.consumes(), putMapping.produces()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.RequestAttribute; /** * Use RequestAttribute to annotate a Form parameter. * * NOTICE: In spring-web, RequestAttribute is used to annotate request attribute, and use RequestParam * to annotate query param and form param. This is implementation based. We can't use RequestParam to express * both query and form in OpenAPI 3.0. And there is no request attribute. */ public class RequestAttributeAnnotationProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return RequestAttribute.class; } @Override public String getParameterName(RequestAttribute annotation) { String value = annotation.value(); if (value.isEmpty()) { value = annotation.name(); } if (StringUtils.isNotEmpty(value)) { return value; } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, RequestAttribute annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.FORM); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.RequestBody; public class RequestBodyAnnotationProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return RequestBody.class; } @Override public String getParameterName(RequestBody annotation) { return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, RequestBody annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.BODY); parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.ValueConstants; public class RequestHeaderAnnotationProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return RequestHeader.class; } @Override public String getParameterName(RequestHeader annotation) { String value = annotation.value(); if (value.isEmpty()) { value = annotation.name(); } if (StringUtils.isNotEmpty(value)) { return value; } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, RequestHeader annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.HEADER); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); if (!ValueConstants.DEFAULT_NONE.equals(annotation.defaultValue())) { parameterGenerator.getParameterGeneratorContext() .setDefaultValue(annotation.defaultValue()); // if default value is set, must be required false. parameterGenerator.getParameterGeneratorContext().setRequired(false); } } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import java.util.Arrays; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; public class RequestMappingClassAnnotationProcessor implements ClassAnnotationProcessor { @Override public Type getProcessType() { return RequestMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, RequestMapping requestMapping) { this.processMethod(requestMapping.method(), swaggerGenerator); // path/value是等同的 this.processPath(requestMapping.path(), swaggerGenerator); this.processPath(requestMapping.value(), swaggerGenerator); if (requestMapping.consumes().length > 0) { swaggerGenerator.getSwaggerGeneratorContext().updateConsumes(Arrays.asList(requestMapping.consumes())); } if (requestMapping.produces().length > 0) { swaggerGenerator.getSwaggerGeneratorContext().updateProduces(Arrays.asList(requestMapping.produces())); } } protected void processPath(String[] paths, SwaggerGenerator swaggerGenerator) { if (null == paths || paths.length == 0) { return; } // swagger仅支持配一个basePath if (paths.length > 1) { throw new IllegalStateException( String.format("not support multi path, class=%s.", swaggerGenerator.getClazz().getName())); } swaggerGenerator.setBasePath(paths[0]); } protected void processMethod(RequestMethod[] requestMethods, SwaggerGenerator swaggerGenerator) { if (null == requestMethods || requestMethods.length == 0) { return; } if (requestMethods.length > 1) { throw new IllegalStateException( String.format("not support multi http method, class=%s.", swaggerGenerator.getClazz().getName())); } swaggerGenerator.setHttpMethod(requestMethods[0].name()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; public class RequestMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { @Override public Type getProcessType() { return RequestMapping.class; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, RequestMapping requestMapping) { if (requestMapping.method().length > 1) { throw new IllegalStateException("not allowed multi http method."); } doProcess(operationGenerator, requestMapping.path(), requestMapping.value(), requestMapping.method().length == 0 ? (StringUtils.isEmpty(operationGenerator.getHttpMethod()) ? RequestMethod.GET : null) : requestMapping.method()[0], requestMapping.consumes(), requestMapping.produces()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamParameterProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ValueConstants; /** * Use RequestParam to annotate a Query parameter. * * NOTICE: In spring-web, RequestParam is used to annotate query param and form param. * This is implementation based. We can't use RequestParam to express * both query and form in OpenAPI 3.0. */ public class RequestParamParameterProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return RequestParam.class; } @Override public String getParameterName(RequestParam annotation) { String value = annotation.value(); if (value.isEmpty()) { value = annotation.name(); } if (StringUtils.isNotEmpty(value)) { return value; } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, RequestParam annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.QUERY); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); if (!ValueConstants.DEFAULT_NONE.equals(annotation.defaultValue())) { parameterGenerator.getParameterGeneratorContext() .setDefaultValue(annotation.defaultValue()); // if default value is set, must be required false. parameterGenerator.getParameterGeneratorContext().setRequired(false); } } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.springframework.web.bind.annotation.RequestPart; public class RequestPartAnnotationProcessor extends SpringmvcParameterAnnotationsProcessor { @Override public Type getProcessType() { return RequestPart.class; } @Override public String getParameterName(RequestPart annotation) { String value = annotation.value(); if (value.isEmpty()) { value = annotation.name(); } if (StringUtils.isNotEmpty(value)) { return value; } return null; } @Override public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ParameterGenerator parameterGenerator, RequestPart annotation) { parameterGenerator.setHttpParameterType(HttpParameterType.FORM); if (StringUtils.isNotEmpty(getParameterName(annotation))) { parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); } parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.RestController; public class RestControllerClassAnnotationProcessor implements ClassAnnotationProcessor { @Override public Type getProcessType() { return RestController.class; } @Override public void process(SwaggerGenerator swaggerGenerator, RestController restController) { if (SwaggerUtils.getBasePath(swaggerGenerator.getOpenAPI()) == null) { swaggerGenerator.setBasePath("/"); } } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor; /** * A generic class to help classify implementations. */ public abstract class SpringmvcParameterAnnotationsProcessor implements ParameterAnnotationProcessor { } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.parameter; import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; public class MultipartFileArrayProcessor extends PartArrayParameterTypeProcessor { @Override public JavaType getProcessType() { return TypeFactory.defaultInstance().constructType(MultipartFile[].class); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.parameter; import java.util.List; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; public class MultipartFileListProcessor extends PartArrayParameterTypeProcessor { @Override public JavaType getProcessType() { return TypeFactory.defaultInstance().constructType( ParameterizedTypeUtil.make(List.class, MultipartFile.class)); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.parameter; import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartParameterTypeProcessor; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; public class MultipartFileTypeProcessor extends PartParameterTypeProcessor { @Override public JavaType getProcessType() { return TypeFactory.defaultInstance().constructType(MultipartFile.class); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.response; import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; import org.springframework.http.ResponseEntity; public class ResponseEntityProcessor extends DefaultResponseTypeProcessor { public ResponseEntityProcessor() { extractActualType = true; } @Override public Class getProcessType() { return ResponseEntity.class; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.property.creator; import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator; import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.models.media.FileSchema; import io.swagger.v3.oas.models.media.Schema; public class MultipartFilePropertyCreator implements PropertyCreator { private final Class[] classes = {MultipartFile.class}; @Override public Schema createProperty() { return new FileSchema(); } @Override public Class[] classes() { return classes; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.property.creator.MultipartFilePropertyCreator ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RestControllerClassAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingClassAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.GetMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.DeleteMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PostMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PutMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PatchMappingMethodAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.CookieValueAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PathVariableAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestBodyAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestParamParameterProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestHeaderAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestPartAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestAttributeAnnotationProcessor ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileTypeProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileArrayProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileListProcessor ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.processor.response.ResponseEntityProcessor ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGeneratorFactory ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping(path = "c") public class ClassMethodNoHttpMethod { @RequestMapping(path = "m") public void noHttpMethod() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping public class ClassMethodNoPath { @RequestMapping(method = RequestMethod.GET) public void noPath() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "Echo", method = {RequestMethod.GET, RequestMethod.POST}) public class ClassMultiHttpMethod { } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping(path = {"a", "b"}) public class ClassMultiPath { } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/Echo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; import jakarta.ws.rs.core.MediaType; @RequestMapping( path = "Echo", method = {RequestMethod.PUT}, consumes = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}, produces = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public class Echo { @RequestMapping("emptyPath") public void emptyPath() { } @RequestMapping("inheritHttpMethod") public void inheritHttpMethod(int query) { } @RequestMapping("cookie") public void cookie(@CookieValue(value = "cookie", required = false) int cookie) { } @RequestMapping("rawJsonStringMethod") public void rawJsonStringMethod(@RawJsonRequestBody String jsonInput) { } @RequestMapping("enumBody") public void enumBody(@RequestBody Color color) { } @RequestMapping("asyncResponseEntity") public CompletableFuture>> asyncResponseEntity() { return null; } @RequestMapping("testResponseEntityOptional") public ResponseEntity> testResponseEntityOptional() { return null; } @RequestMapping("testCompletableFutureResponseEntityOptional") public CompletableFuture>> testCompletableFutureResponseEntityOptional() { return null; } @RequestMapping(value = "part", consumes = MediaType.MULTIPART_FORM_DATA) public void part(@RequestPart MultipartFile part) { } @RequestMapping(value = "partArray", consumes = MediaType.MULTIPART_FORM_DATA) public void partArray(@RequestPart MultipartFile[] part) { } @RequestMapping(value = "partList", consumes = MediaType.MULTIPART_FORM_DATA) public void partList(@RequestPart List part) { } @RequestMapping(value = "partAnnotation", consumes = MediaType.MULTIPART_FORM_DATA) public void partAnnotation(@RequestPart MultipartFile part) { } @RequestMapping(value = "partArrayAnnotation", consumes = MediaType.MULTIPART_FORM_DATA) public void partArrayAnnotation(@RequestPart MultipartFile[] part) { } @RequestMapping(value = "partListAnnotation", consumes = MediaType.MULTIPART_FORM_DATA) public void partListAnnotation(@RequestPart List part) { } @RequestMapping("nestedListString") public List> nestedListString(@RequestBody List> param) { return param; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodDefaultParameter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "MethodDefaultParameter") public class MethodDefaultParameter { @RequestMapping(method = RequestMethod.PUT, path = "usingRequestMapping") public void usingRequestMapping(int query) { } @GetMapping(path = "usingGetMapping") public void usingGetMapping(int query) { } @PutMapping(path = "usingPutMapping") public void usingPutMapping(int query) { } @PostMapping(path = "usingPostMapping") public void usingPostMapping(int query) { } @PatchMapping(path = "usingPatchMapping") public void usingPatchMapping(int query) { } @DeleteMapping(path = "usingDeleteMapping") public void usingDeleteMapping(int query) { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodEmptyPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import io.swagger.v3.oas.annotations.Operation; @RequestMapping(path = "MethodEmptyPath") public class MethodEmptyPath { @GetMapping public void usingGetMapping() { } @PostMapping public void usingPostMapping() { } @PutMapping public void usingPutMapping() { } @DeleteMapping public void usingDeleteMapping() { } @PatchMapping public void usingPatchMapping() { } // this will be ignored in the generation of service contract // as ApiOperation is not a restful annotation @Operation(summary = "") public void ignoredNonRestful() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMixupAnnotations.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import java.time.LocalDateTime; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; @SuppressWarnings("unused") @RequestMapping(path = "MethodMixupAnnotations") public class MethodMixupAnnotations { @RequestMapping( path = "usingRequestMapping/{targetName}", method = {RequestMethod.POST}, consumes = {"text/plain", "application/json"}, produces = {"text/plain", "application/json"}) public String usingRequestMapping(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, @RequestParam(name = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @GetMapping( path = "usingGetMapping/{targetName}", consumes = {"text/plain", "application/json"}, produces = {"text/plain", "application/json"}) public String usingGetMapping(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, @RequestParam(name = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @PutMapping( path = "usingPutMapping/{targetName}", consumes = {"text/plain", "application/json"}, produces = {"text/plain", "application/json"}) public String usingPutMapping(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, @RequestParam(name = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @PostMapping( path = "usingPostMapping/{targetName}", consumes = {"text/plain", "application/json"}, produces = {"text/plain", "application/json"}) public String usingPostMapping(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, @RequestParam(name = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @PatchMapping( path = "usingPatchMapping/{targetName}", consumes = {"text/plain", "application/json"}, produces = {"text/plain", "application/json"}) public String usingPatchMapping(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, @RequestParam(name = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @DeleteMapping( path = "usingDeleteMapping/{targetName}", consumes = {"text/plain", "application/json"}, produces = {"text/plain", "application/json"}) public String usingDeleteMapping(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, @RequestParam(name = "word") String word) { return String.format("%s %s %s %s", srcUser.name, header, targetName, word); } @PostMapping(path = "/uploadFileAndAttribute") public String uploadFileAndAttribute(@RequestPart(name = "file") MultipartFile file, @RequestPart(name = "attribute") String attribute) { return null; } @PostMapping(path = "/uploadFilesAndAttribute") public String uploadFilesAndAttribute(@RequestPart(name = "files") MultipartFile[] files, @RequestPart(name = "attribute") String attribute) { return null; } @GetMapping(path = "/reduce") @Parameters({@Parameter(name = "a", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY)}) public int reduce(HttpServletRequest request, @CookieValue(name = "b") int b) { return 0; } @RequestMapping(path = "/defaultQueryParam", method = RequestMethod.POST) public String defaultQueryParam(String prefix, @RequestBody User user) { return null; } @GetMapping(path = "/diffNames") @Operation(summary = "differentName", operationId = "differentName") public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) { return a * 2 + b; } @GetMapping(path = "/bytes") public byte[] bytes(@RequestBody byte[] value) { return null; } @PostMapping(path = "/upload", produces = MediaType.TEXT_PLAIN_VALUE) public String fileUpload(@RequestPart(name = "file1") MultipartFile file1, @RequestPart(name = "someFile") Part file2) { return null; } @PostMapping(path = "/testImplicitForm") @io.swagger.v3.oas.annotations.parameters.RequestBody( content = {@Content(mediaType = SwaggerConst.FORM_MEDIA_TYPE, schema = @Schema(name = "form1", implementation = String.class, nullable = false, description = "a required form param")), @Content(mediaType = SwaggerConst.FORM_MEDIA_TYPE, schema = @Schema(name = "form2", implementation = String.class, nullable = true, description = "an optional form param"))}) public String testImplicitForm(HttpServletRequest request) { return null; } @GetMapping("/testDefaultValue") public String testDefaultValue(@RequestParam(name = "e", required = false) int e, @RequestHeader(name = "a", defaultValue = "20") int a, @CookieValue(name = "b", defaultValue = "bobo") String b, @RequestParam(name = "c", defaultValue = "40") Integer c, @Min(value = 20) @Max(value = 30) @RequestParam(name = "d", required = false) int d) { return "Hello " + a + b + c + d + e; } @GetMapping(path = "/testLocalDateTime") public LocalDateTime testLocalDateTime(@RequestParam("date") LocalDateTime date) { return date; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMultiPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping public class MethodMultiPath { @RequestMapping(method = RequestMethod.GET, path = {"a", "b"}) public void usingRequestMapping() { } @GetMapping(path = {"a", "b"}) public void usingGetMapping() { } @PutMapping(path = {"a", "b"}) public void usingPutMapping() { } @PostMapping(path = {"a", "b"}) public void usingPostMapping() { } @PatchMapping(path = {"a", "b"}) public void usingPatchMapping() { } @DeleteMapping(path = {"a", "b"}) public void usingDeleteMapping() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodResponseEntity.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import java.util.List; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "MethodResponseEntity", method = RequestMethod.POST) public class MethodResponseEntity { @RequestMapping(method = RequestMethod.PUT, path = "usingRequestMapping") public ResponseEntity> usingRequestMapping() { return null; } @GetMapping(path = "usingGetMapping") public ResponseEntity> usingGetMapping() { return null; } @PutMapping(path = "usingPutMapping") public ResponseEntity> usingPutMapping() { return null; } @PostMapping(path = "usingPostMapping") public ResponseEntity> usingPostMapping() { return null; } @PatchMapping(path = "usingPatchMapping") public ResponseEntity> usingPatchMapping() { return null; } @DeleteMapping(path = "usingDeleteMapping") public ResponseEntity> usingDeleteMapping() { return null; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MultiDefaultPath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "abc", method = RequestMethod.POST) public class MultiDefaultPath { @GetMapping public void p1() { } @RequestMapping(method = RequestMethod.GET) public void p2() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema; import org.apache.servicecomb.swagger.generator.springmvc.model.RestControllerWithPathSchema; import org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget; import org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget_ValueOverWritePath; import org.junit.jupiter.api.Test; public class TestSpringmvc { @Test public void testMultiDefaultPath() { UnitTestSwaggerUtils.testException( "Duplicate operation path detected. method=org.apache.servicecomb.swagger.generator.springmvc.MultiDefaultPath:p2.", MultiDefaultPath.class); } @Test public void testResponseEntity() { UnitTestSwaggerUtils.testSwagger("schemas/responseEntity.yaml", MethodResponseEntity.class); } @Test public void testEcho() { UnitTestSwaggerUtils.testSwagger("schemas/echo.yaml", Echo.class); } @Test public void testMixupAnnotations() { UnitTestSwaggerUtils.testSwagger("schemas/mixupAnnotations.yaml", MethodMixupAnnotations.class); } @Test public void testDefaultParameter() { UnitTestSwaggerUtils.testSwagger("schemas/defaultParameter.yaml", MethodDefaultParameter.class); } @Test public void testClassMethodNoPath() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=ClassMethodNoPath:noPath, " + "cause=Path must not both be empty in class and method", ClassMethodNoPath.class, "noPath"); } @Test public void testClassMethodNoHttpMethod() { UnitTestSwaggerUtils .testSwagger("schemas/requestMappingHttpMethod.yaml", ClassMethodNoHttpMethod.class, "noHttpMethod"); } @Test public void testClassMultiHttpMethod() { UnitTestSwaggerUtils.testException( "not support multi http method, class=org.apache.servicecomb.swagger.generator.springmvc.ClassMultiHttpMethod.", ClassMultiHttpMethod.class); } @Test public void testMethodMultiPathUsingRequestMapping() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=MethodMultiPath:usingRequestMapping, cause=not allowed multi path.", MethodMultiPath.class, "usingRequestMapping"); } @Test public void testMethodMultiPathUsingGetMapping() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=MethodMultiPath:usingGetMapping, cause=not allowed multi path.", MethodMultiPath.class, "usingGetMapping"); } @Test public void testMethodMultiPathUsingPutMapping() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=MethodMultiPath:usingPutMapping, cause=not allowed multi path.", MethodMultiPath.class, "usingPutMapping"); } @Test public void testMethodMultiPathUsingPostMapping() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=MethodMultiPath:usingPostMapping, cause=not allowed multi path.", MethodMultiPath.class, "usingPostMapping"); } @Test public void testMethodMultiPathUsingPatchMapping() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=MethodMultiPath:usingPatchMapping, cause=not allowed multi path.", MethodMultiPath.class, "usingPatchMapping"); } @Test public void testMethodMultiPathUsingDeleteMapping() { UnitTestSwaggerUtils.testException( "Generate swagger operation failed, method=MethodMultiPath:usingDeleteMapping, cause=not allowed multi path.", MethodMultiPath.class, "usingDeleteMapping"); } @Test public void testClassMultiPath() { UnitTestSwaggerUtils.testException( "not support multi path, class=org.apache.servicecomb.swagger.generator.springmvc.ClassMultiPath.", ClassMultiPath.class); } @Test public void testDefaultParameterSchema() { UnitTestSwaggerUtils.testSwagger("schemas/DefaultParameterSchema.yaml", DefaultParameterSchema.class); } @Test public void testRestControllerWithPathSchema() { UnitTestSwaggerUtils.testSwagger("schemas/RestControllerWithPathSchema.yaml", RestControllerWithPathSchema.class); } @Test public void swaggerTestTarget() { UnitTestSwaggerUtils.testSwagger("schemas/swaggerTestTarget.yaml", SwaggerTestTarget.class); } @Test public void swaggerTestTarget_ValueOverWritePath() { UnitTestSwaggerUtils .testSwagger("schemas/swaggerTestTarget_ValueOverWritePath.yaml", SwaggerTestTarget_ValueOverWritePath.class); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.springmvc.model.Generic; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import io.swagger.v3.oas.models.OpenAPI; import jakarta.ws.rs.core.MediaType; public class TestTwoSameNameModels { private static final String SCHEMA_CONTENT = """ openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.TestTwoSameNameModels$TwoSameModelService version: 1.0.0 servers: - url: /TwoSameModelService paths: /sameGeneric: post: operationId: genericService requestBody: content: application/json: schema: $ref: "#/components/schemas/GenericSameModel" required: true x-name: param responses: "200": description: response of 200 content: application/json: schema: $ref: "#/components/schemas/GenericSameModel" /same: post: operationId: service requestBody: content: application/json: schema: $ref: "#/components/schemas/SameModel" required: true x-name: param responses: "200": description: response of 200 content: application/json: schema: $ref: "#/components/schemas/SameModel" components: schemas: GenericSameModel: type: object properties: data: $ref: "#/components/schemas/SameModel" x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.Generic SameModel: type: object properties: name: type: string x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel """; interface TwoSameModelService { @RequestMapping( path = "/same", method = {RequestMethod.POST}, consumes = {MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON}) org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel service (@RequestBody org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel param); @RequestMapping( path = "/sameGeneric", method = {RequestMethod.POST}, consumes = {MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON}) Generic genericService (@RequestBody Generic param); } interface SameModelService { @RequestMapping( path = "/same", method = {RequestMethod.POST}, consumes = {MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON}) org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel service (@RequestBody org.apache.servicecomb.swagger.generator.springmvc.model.same2.SameModel param); } interface SameModelThrowService { @RequestMapping( path = "/same", method = {RequestMethod.POST}, consumes = {MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON}) org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModelThrow service (@RequestBody org.apache.servicecomb.swagger.generator.springmvc.model.same2.SameModelThrow param); } @Test public void testTwoSameModelWork() { SwaggerGenerator generator = SwaggerGenerator.create(TwoSameModelService.class); OpenAPI openAPI = generator.generate(); Assertions.assertEquals(SCHEMA_CONTENT, SwaggerUtils.swaggerToString(openAPI)); } @Test public void testSameModelThrowThrowException() { SwaggerGenerator generator = SwaggerGenerator.create(SameModelThrowService.class); Assertions.assertThrows(IllegalStateException.class, () -> generator.generate()); } @Test public void testSameModelThrowException() { SwaggerGenerator generator = SwaggerGenerator.create(SameModelService.class); Assertions.assertThrows(IllegalStateException.class, () -> generator.generate()); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/DefaultParameterSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; import java.util.List; import java.util.Map; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import jakarta.ws.rs.core.MediaType; @RestController public class DefaultParameterSchema { @GetMapping("/testSimpleParam") public String testSimpleParam(String strParam) { return strParam; } @GetMapping("/testObjectParam") public String testObjectParam(TestParam objParam) { return objParam.toString(); } @GetMapping("/testUnsupportedParamType") public String testUnsupportedParamType(int i, List integerList, Map stringMap) { return null; } @RequestMapping(path = "testSingleMediaType", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON) public String testSingleMediaType(String input) { return input; } @RequestMapping(path = "testMultipleMediaType", method = RequestMethod.PUT, consumes = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}, produces = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) public String testMultipleMediaType(String input) { return input; } @RequestMapping(path = "testBlankMediaType", method = RequestMethod.POST) public String testBlankMediaType(String input) { return input; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/Generic.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; public class Generic { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/prefix") public class RestControllerWithPathSchema { @GetMapping("/testSimpleParam") public String testSimpleParam(String strParam) { return strParam; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; import jakarta.ws.rs.core.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "/test", method = RequestMethod.POST, consumes = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public class SwaggerTestTarget { @RequestMapping public void method() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget_ValueOverWritePath.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(value = "/testValue", path = "/testPath", method = RequestMethod.GET) public class SwaggerTestTarget_ValueOverWritePath { @RequestMapping public void method() { } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestParam.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; public class TestParam { private String name; private int age; public String getName() { return name; } public TestParam setName(String name) { this.name = name; return this; } public int getAge() { return age; } public TestParam setAge(int age) { this.age = age; return this; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestProducer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model; import jakarta.ws.rs.core.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "/") public class TestProducer { @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_XML, produces = MediaType.APPLICATION_XML) public String testSingleMediaType(String input) { return input; } @RequestMapping(method = RequestMethod.PUT, consumes = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}, produces = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) public String testMultipleMediaType(String input) { return input; } @RequestMapping(method = RequestMethod.POST, consumes = "", produces = "") public String testBlankMediaType(String input) { return input; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model.same1; public class SameModel { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModelThrow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model.same1; public class SameModelThrow { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModel.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model.same2; public class SameModel { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModelThrow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.model.same2; public class SameModelThrow { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; import java.util.List; import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.junit.jupiter.api.Test; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; public class RequestPartAnnotationProcessorTest { @Test public void testDemoRest() { UnitTestSwaggerUtils.testSwagger("schemas/DemoRest.yaml", DemoRest.class); } @RequestMapping("/") public static class DemoRest { @RequestMapping(method = RequestMethod.POST, path = "/fun") public void fun(@RequestPart("stringParam") String stringParam, @RequestPart(name = "intParam") int intParam, @RequestPart("stringParamArray") String[] stringParamArray, @RequestPart("stringParamCollection") List stringParamCollection, @RequestPart("file") MultipartFile file, @RequestPart("fileArray") MultipartFile[] fileArray, @RequestPart("fileCollection") List fileCollection) { } } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.generator.springmvc.property.creator; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.models.media.FileSchema; import io.swagger.v3.oas.models.media.Schema; public class MultipartFilePropertyCreatorTest { private final MultipartFilePropertyCreator multipartFilePropertyCreator = new MultipartFilePropertyCreator(); @Test public void createProperty() { Schema property = multipartFilePropertyCreator.createProperty(); MatcherAssert.assertThat(property, Matchers.instanceOf(FileSchema.class)); } @Test public void classes() { Class[] classes = multipartFilePropertyCreator.classes(); MatcherAssert.assertThat(classes, Matchers.arrayContaining(MultipartFile.class)); } } ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema version: 1.0.0 servers: - url: / paths: /testBlankMediaType: post: operationId: testBlankMediaType parameters: - name: input in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testMultipleMediaType: put: operationId: testMultipleMediaType parameters: - name: input in: query schema: type: string responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string /testObjectParam: get: operationId: testObjectParam parameters: - name: name in: query schema: type: string - name: age in: query schema: type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testSimpleParam: get: operationId: testSimpleParam parameters: - name: strParam in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testSingleMediaType: post: operationId: testSingleMediaType parameters: - name: input in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string /testUnsupportedParamType: get: operationId: testUnsupportedParamType parameters: - name: i in: query schema: type: integer format: int32 - name: integerList in: query schema: type: array items: $ref: "#/components/schemas/TestParam" - name: stringMap in: query schema: type: object additionalProperties: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string components: schemas: TestParam: type: object properties: name: type: string age: type: integer format: int32 x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.TestParam ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestPartAnnotationProcessorTest$DemoRest version: 1.0.0 servers: - url: / paths: /fun: post: operationId: fun requestBody: content: multipart/form-data: schema: type: object properties: stringParam: type: string intParam: type: integer format: int32 stringParamArray: type: array items: type: string stringParamCollection: type: array items: type: string file: type: string format: binary fileArray: type: array items: type: string format: binary fileCollection: type: array items: type: string format: binary responses: "200": description: response of 200 components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/MethodEmptyPath.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodEmptyPath" x-java-interface: "gen.cse.ms.ut.MethodEmptyPathIntf" basePath: "/MethodEmptyPath" consumes: - "application/json" produces: - "application/json" paths: /: get: operationId: "usingGetMapping" parameters: [] responses: "200": description: "response of 200" post: operationId: "usingPostMapping" parameters: [] responses: "200": description: "response of 200" put: operationId: "usingPutMapping" parameters: [] responses: "200": description: "response of 200" delete: operationId: "usingDeleteMapping" parameters: [] responses: "200": description: "response of 200" patch: operationId: "usingPatchMapping" parameters: [] responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.RestControllerWithPathSchema version: 1.0.0 servers: - url: /prefix paths: /testSimpleParam: get: operationId: testSimpleParam parameters: - name: strParam in: query schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/asyncResponseEntity.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "asyncResponseEntity" parameters: [] responses: "200": description: "response of 200" schema: type: "array" items: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/cookie.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "cookie" parameters: - name: "cookie" in: "cookie" required: false type: "integer" format: "int32" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodDefaultParameter version: 1.0.0 servers: - url: MethodDefaultParameter paths: /usingDeleteMapping: delete: operationId: usingDeleteMapping parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 /usingGetMapping: get: operationId: usingGetMapping parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 /usingPatchMapping: patch: operationId: usingPatchMapping parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 /usingPostMapping: post: operationId: usingPostMapping parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 /usingPutMapping: put: operationId: usingPutMapping parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 /usingRequestMapping: put: operationId: usingRequestMapping parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo version: 1.0.0 servers: - url: Echo paths: /asyncResponseEntity: put: operationId: asyncResponseEntity responses: "200": description: response of 200 content: application/json: schema: type: array items: type: string text/plain: schema: type: array items: type: string /cookie: put: operationId: cookie parameters: - name: cookie in: cookie required: false schema: type: integer format: int32 responses: "200": description: response of 200 /emptyPath: put: operationId: emptyPath responses: "200": description: response of 200 /enumBody: put: operationId: enumBody requestBody: content: application/json: schema: type: string enum: - RED - YELLOW - BLUE text/plain: schema: type: string enum: - RED - YELLOW - BLUE required: true x-name: color responses: "200": description: response of 200 /inheritHttpMethod: put: operationId: inheritHttpMethod parameters: - name: query in: query schema: type: integer format: int32 responses: "200": description: response of 200 /nestedListString: put: operationId: nestedListString requestBody: content: application/json: schema: type: array items: type: array items: type: string text/plain: schema: type: array items: type: array items: type: string required: true x-name: param responses: "200": description: response of 200 content: application/json: schema: type: array items: type: array items: type: string text/plain: schema: type: array items: type: array items: type: string /part: put: operationId: part requestBody: content: multipart/form-data: schema: type: object properties: part: type: string format: binary responses: "200": description: response of 200 /partAnnotation: put: operationId: partAnnotation requestBody: content: multipart/form-data: schema: type: object properties: part: type: string format: binary responses: "200": description: response of 200 /partArray: put: operationId: partArray requestBody: content: multipart/form-data: schema: type: object properties: part: type: array items: type: string format: binary responses: "200": description: response of 200 /partArrayAnnotation: put: operationId: partArrayAnnotation requestBody: content: multipart/form-data: schema: type: object properties: part: type: array items: type: string format: binary responses: "200": description: response of 200 /partList: put: operationId: partList requestBody: content: multipart/form-data: schema: type: object properties: part: type: array items: type: string format: binary responses: "200": description: response of 200 /partListAnnotation: put: operationId: partListAnnotation requestBody: content: multipart/form-data: schema: type: object properties: part: type: array items: type: string format: binary responses: "200": description: response of 200 /rawJsonStringMethod: put: operationId: rawJsonStringMethod requestBody: content: application/json: schema: type: string text/plain: schema: type: string required: true x-raw-json: true x-name: jsonInput responses: "200": description: response of 200 /testCompletableFutureResponseEntityOptional: put: operationId: testCompletableFutureResponseEntityOptional responses: "200": description: response of 200 content: application/json: schema: type: string text/plain: schema: type: string /testResponseEntityOptional: put: operationId: testResponseEntityOptional responses: "200": description: response of 200 content: application/json: schema: type: string text/plain: schema: type: string components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/emptyPath.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo version: 1.0.0 servers: - url: Echo paths: /: put: operationId: emptyPath responses: "200": description: response of 200 ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/enumBody.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "enumBody" parameters: - in: "body" name: "color" required: true schema: type: "string" enum: - "RED" - "YELLOW" - "BLUE" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/inheritHttpMethod.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "inheritHttpMethod" parameters: - name: "query" in: "query" required: false type: "integer" format: "int32" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodMixupAnnotations version: 1.0.0 servers: - url: MethodMixupAnnotations paths: /bytes: get: operationId: bytes requestBody: content: application/json: schema: type: string format: byte application/protobuf: schema: type: string format: byte text/plain: schema: type: string format: byte required: true x-name: value responses: "200": description: response of 200 content: application/json: schema: type: string format: byte application/protobuf: schema: type: string format: byte text/plain: schema: type: string format: byte /defaultQueryParam: post: operationId: defaultQueryParam parameters: - name: prefix in: query schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" application/protobuf: schema: $ref: "#/components/schemas/User" text/plain: schema: $ref: "#/components/schemas/User" required: true x-name: user responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /diffNames: get: summary: differentName operationId: differentName parameters: - name: x in: query required: true schema: type: integer format: int32 - name: "y" in: query required: true schema: type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: integer format: int32 application/protobuf: schema: type: integer format: int32 text/plain: schema: type: integer format: int32 /upload: post: operationId: fileUpload requestBody: content: multipart/form-data: schema: type: object properties: file1: type: string format: binary someFile: type: string format: binary responses: "200": description: response of 200 content: text/plain: schema: type: string /reduce: get: operationId: reduce parameters: - name: b in: cookie required: true schema: type: integer format: int32 - name: a in: query required: false schema: type: string responses: "200": description: response of 200 content: application/json: schema: type: integer format: int32 application/protobuf: schema: type: integer format: int32 text/plain: schema: type: integer format: int32 /testDefaultValue: get: operationId: testDefaultValue parameters: - name: e in: query required: false schema: type: integer format: int32 - name: a in: header required: false schema: type: integer format: int32 default: 20 - name: b in: cookie required: false schema: type: string default: bobo - name: c in: query required: false schema: type: integer format: int32 default: 40 - name: d in: query required: false schema: maximum: 30 minimum: 20 type: integer format: int32 responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testImplicitForm: post: operationId: testImplicitForm requestBody: content: application/x-www-form-urlencoded: schema: properties: form1: type: string description: a required form param nullable: false example: "" form2: type: string description: an optional form param nullable: true example: "" responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /testLocalDateTime: get: operationId: testLocalDateTime parameters: - name: date in: query required: true schema: type: string format: date-time responses: "200": description: response of 200 content: application/json: schema: type: string format: date-time application/protobuf: schema: type: string format: date-time text/plain: schema: type: string format: date-time /uploadFileAndAttribute: post: operationId: uploadFileAndAttribute requestBody: content: multipart/form-data: schema: type: object properties: file: type: string format: binary attribute: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /uploadFilesAndAttribute: post: operationId: uploadFilesAndAttribute requestBody: content: multipart/form-data: schema: type: object properties: files: type: array items: type: string format: binary attribute: type: string responses: "200": description: response of 200 content: application/json: schema: type: string application/protobuf: schema: type: string text/plain: schema: type: string /usingDeleteMapping/{targetName}: delete: operationId: usingDeleteMapping parameters: - name: header in: header required: true schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query required: true schema: type: string requestBody: content: text/plain: schema: $ref: "#/components/schemas/User" application/json: schema: $ref: "#/components/schemas/User" required: true x-name: srcUser responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string /usingGetMapping/{targetName}: get: operationId: usingGetMapping parameters: - name: header in: header required: true schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query required: true schema: type: string requestBody: content: text/plain: schema: $ref: "#/components/schemas/User" application/json: schema: $ref: "#/components/schemas/User" required: true x-name: srcUser responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string /usingPatchMapping/{targetName}: patch: operationId: usingPatchMapping parameters: - name: header in: header required: true schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query required: true schema: type: string requestBody: content: text/plain: schema: $ref: "#/components/schemas/User" application/json: schema: $ref: "#/components/schemas/User" required: true x-name: srcUser responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string /usingPostMapping/{targetName}: post: operationId: usingPostMapping parameters: - name: header in: header required: true schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query required: true schema: type: string requestBody: content: text/plain: schema: $ref: "#/components/schemas/User" application/json: schema: $ref: "#/components/schemas/User" required: true x-name: srcUser responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string /usingPutMapping/{targetName}: put: operationId: usingPutMapping parameters: - name: header in: header required: true schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query required: true schema: type: string requestBody: content: text/plain: schema: $ref: "#/components/schemas/User" application/json: schema: $ref: "#/components/schemas/User" required: true x-name: srcUser responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string /usingRequestMapping/{targetName}: post: operationId: usingRequestMapping parameters: - name: header in: header required: true schema: type: string - name: targetName in: path required: true schema: type: string - name: word in: query required: true schema: type: string requestBody: content: text/plain: schema: $ref: "#/components/schemas/User" application/json: schema: $ref: "#/components/schemas/User" required: true x-name: srcUser responses: "200": description: response of 200 content: text/plain: schema: type: string application/json: schema: type: string components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/nestedListString.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /nestedListString: post: operationId: "nestedListString" parameters: - in: "body" name: "param" required: true schema: type: "array" items: type: "array" items: type: "string" responses: "200": description: "response of 200" schema: type: "array" items: type: "array" items: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/part.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "part" consumes: - "multipart/form-data" parameters: - name: "part" in: "formData" required: false type: "file" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partAnnotation.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "partAnnotation" consumes: - "multipart/form-data" parameters: - name: "part" in: "formData" required: true type: "file" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArray.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "partArray" consumes: - "multipart/form-data" parameters: - name: "part" in: "formData" required: false type: "array" items: type: "file" collectionFormat: "multi" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArrayAnnotation.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "partArrayAnnotation" consumes: - "multipart/form-data" parameters: - name: "part" in: "formData" required: true type: "array" items: type: "file" collectionFormat: "multi" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partList.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "partList" consumes: - "multipart/form-data" parameters: - name: "part" in: "formData" required: false type: "array" items: type: "file" collectionFormat: "multi" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partListAnnotation.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "partListAnnotation" consumes: - "multipart/form-data" parameters: - name: "part" in: "formData" required: true type: "array" items: type: "file" collectionFormat: "multi" responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/rawJsonStringMethod.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "rawJsonStringMethod" parameters: - in: "body" name: "jsonInput" required: true schema: type: "string" x-raw-json: true responses: "200": description: "response of 200" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/requestMappingHttpMethod.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.ClassMethodNoHttpMethod version: 1.0.0 servers: - url: c paths: /m: get: operationId: noHttpMethod responses: "200": description: response of 200 components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodResponseEntity version: 1.0.0 servers: - url: MethodResponseEntity paths: /usingDeleteMapping: delete: operationId: usingDeleteMapping responses: "200": description: response of 200 content: application/json: schema: type: array items: $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: $ref: "#/components/schemas/User" text/plain: schema: type: array items: $ref: "#/components/schemas/User" /usingGetMapping: get: operationId: usingGetMapping responses: "200": description: response of 200 content: application/json: schema: type: array items: $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: $ref: "#/components/schemas/User" text/plain: schema: type: array items: $ref: "#/components/schemas/User" /usingPatchMapping: patch: operationId: usingPatchMapping responses: "200": description: response of 200 content: application/json: schema: type: array items: $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: $ref: "#/components/schemas/User" text/plain: schema: type: array items: $ref: "#/components/schemas/User" /usingPostMapping: post: operationId: usingPostMapping responses: "200": description: response of 200 content: application/json: schema: type: array items: $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: $ref: "#/components/schemas/User" text/plain: schema: type: array items: $ref: "#/components/schemas/User" /usingPutMapping: put: operationId: usingPutMapping responses: "200": description: response of 200 content: application/json: schema: type: array items: $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: $ref: "#/components/schemas/User" text/plain: schema: type: array items: $ref: "#/components/schemas/User" /usingRequestMapping: put: operationId: usingRequestMapping responses: "200": description: response of 200 content: application/json: schema: type: array items: $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: $ref: "#/components/schemas/User" text/plain: schema: type: array items: $ref: "#/components/schemas/User" components: schemas: User: type: object properties: name: type: string friends: type: array items: $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget version: 1.0.0 servers: - url: /test paths: /: post: operationId: method responses: "200": description: response of 200 components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- openapi: 3.0.1 info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget_ValueOverWritePath version: 1.0.0 servers: - url: /testValue paths: /: get: operationId: method responses: "200": description: response of 200 components: {} ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testBlankMediaType.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.TestProducer" x-java-interface: "gen.cse.ms.ut.TestProducerIntf" basePath: "/" consumes: - "application/json" produces: - "application/json" paths: /: post: operationId: "testBlankMediaType" parameters: - name: "input" in: "query" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testCompletableFutureResponseEntityOptional.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "testCompletableFutureResponseEntityOptional" parameters: [] responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testMultipleMediaType.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.TestProducer" x-java-interface: "gen.cse.ms.ut.TestProducerIntf" basePath: "/" consumes: - "application/json" produces: - "application/json" paths: /: put: operationId: "testMultipleMediaType" consumes: - "text/plain" - "application/json" produces: - "text/plain" - "application/json" parameters: - name: "input" in: "query" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testObjectParam.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema" x-java-interface: "gen.cse.ms.ut.DefaultParameterSchemaIntf" basePath: "/" consumes: - "application/json" produces: - "application/json" paths: /testObjectParam: get: operationId: "testObjectParam" parameters: - name: "name" in: "query" required: false type: "string" - name: "age" in: "query" required: false type: "integer" format: "int32" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testResponseEntityOptional.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: - "a" - "b" produces: - "a" - "b" paths: /: put: operationId: "testResponseEntityOptional" parameters: [] responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSimpleParam.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema" x-java-interface: "gen.cse.ms.ut.DefaultParameterSchemaIntf" basePath: "/" consumes: - "application/json" produces: - "application/json" paths: /testSimpleParam: get: operationId: "testSimpleParam" parameters: - name: "strParam" in: "query" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSingleMediaType.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- --- swagger: "2.0" info: version: "1.0.0" title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.TestProducer" x-java-interface: "gen.cse.ms.ut.TestProducerIntf" basePath: "/" consumes: - "application/json" produces: - "application/json" paths: /: post: operationId: "testSingleMediaType" consumes: - "application/xml" produces: - "application/xml" parameters: - name: "input" in: "query" required: false type: "string" responses: "200": description: "response of 200" schema: type: "string" ================================================ FILE: swagger/swagger-generator/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger 3.4.0-SNAPSHOT swagger-generator Java Chassis::Swagger::Generator pom generator-core generator-jaxrs generator-springmvc generator-spring-data ================================================ FILE: swagger/swagger-invocation/invocation-core/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-invocation 3.4.0-SNAPSHOT swagger-invocation-core Java Chassis::Swagger::Invocation::Core org.apache.servicecomb swagger-generator-core org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb swagger-generator-jaxrs test org.apache.servicecomb swagger-generator-springmvc test org.jmockit jmockit test ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.google.common.annotations.VisibleForTesting; public class SwaggerConsumer { private Class consumerIntf; // key is consumer method name private final Map operations = new HashMap<>(); public Class getConsumerIntf() { return consumerIntf; } public void setConsumerIntf(Class consumerIntf) { this.consumerIntf = consumerIntf; } public void addOperation(SwaggerConsumerOperation op) { operations.put(op.getConsumerMethod(), op); } @VisibleForTesting public SwaggerConsumerOperation findOperation(String consumerMethodName) { for (Entry operationEntry : operations.entrySet()) { if (operationEntry.getKey().getName().equals(consumerMethodName)) { return operationEntry.getValue(); } } return null; } public SwaggerConsumerOperation findOperation(Method consumerMethod) { return operations.get(consumerMethod); } public Map getOperations() { return operations; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumerOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; public class SwaggerConsumerOperation { private Class consumerClass; private Method consumerMethod; private String[] consumerParameterNames; private SwaggerOperation swaggerOperation; private ArgumentsMapper argumentsMapper; private ConsumerResponseMapper responseMapper; public Method getConsumerMethod() { return consumerMethod; } public void setConsumerMethod(Method consumerMethod) { this.consumerMethod = consumerMethod; this.consumerParameterNames = Arrays.stream(consumerMethod.getParameters()) .map(Parameter::getName) .toArray(String[]::new); } public Class getConsumerClass() { return consumerClass; } public void setConsumerClass(Class consumerClass) { this.consumerClass = consumerClass; } public SwaggerOperation getSwaggerOperation() { return swaggerOperation; } public void setSwaggerOperation(SwaggerOperation swaggerOperation) { this.swaggerOperation = swaggerOperation; } public ArgumentsMapper getArgumentsMapper() { return argumentsMapper; } public void setArgumentsMapper(ArgumentsMapper argumentsMapper) { this.argumentsMapper = argumentsMapper; } public ConsumerResponseMapper getResponseMapper() { return responseMapper; } public void setResponseMapper(ConsumerResponseMapper responseMapper) { this.responseMapper = responseMapper; } public Map toInvocationArguments(Object[] args) { Map arguments = new HashMap<>(); for (int i = 0; i < consumerParameterNames.length; i++) { arguments.put(consumerParameterNames[i], args[i]); } return arguments; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import java.lang.reflect.Method; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentsMapperCreator; import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperCreator; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.OpenAPI; public class SwaggerEnvironment { private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerEnvironment.class); /* * For Consumer, first load swagger from resources. Generate one if not exists. */ public SwaggerConsumer createConsumer(Class consumerIntf, OpenAPI swagger) { swagger = checkAndGenerateSwagger(consumerIntf, swagger); Map, ContextArgumentMapperFactory> contextFactories = SPIServiceUtils .getOrLoadSortedService(ConsumerContextArgumentMapperFactory.class) .stream() .collect(Collectors.toMap(ConsumerContextArgumentMapperFactory::getContextClass, Function.identity())); ResponseMapperFactories consumerResponseMapperFactories = new ResponseMapperFactories<>(ConsumerResponseMapperFactory.class); SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); SwaggerConsumer consumer = new SwaggerConsumer(); consumer.setConsumerIntf(consumerIntf); for (Method consumerMethod : MethodUtils.findSwaggerMethods(consumerIntf)) { String operationId = findOperationId(consumerMethod); SwaggerOperation swaggerOperation = swaggerOperations.findOperation(operationId); if (swaggerOperation == null) { // consumer method set is bigger than contract, it's invalid // but we need to support consumer upgrade before producer, so only log and ignore it. LOGGER.warn("consumer method {}:{} not exist in contract.", consumerIntf.getName(), consumerMethod.getName()); continue; } ConsumerArgumentsMapperCreator creator = new ConsumerArgumentsMapperCreator( Json.mapper().getSerializationConfig(), contextFactories, consumerIntf, consumerMethod, swaggerOperation); ArgumentsMapper argsMapper = creator.createArgumentsMapper(); ConsumerResponseMapper responseMapper = consumerResponseMapperFactories .createResponseMapper(consumerMethod.getGenericReturnType()); SwaggerConsumerOperation op = new SwaggerConsumerOperation(); op.setConsumerClass(consumerIntf); op.setConsumerMethod(consumerMethod); op.setSwaggerOperation(swaggerOperation); op.setArgumentsMapper(argsMapper); op.setResponseMapper(responseMapper); consumer.addOperation(op); } return consumer; } protected String findOperationId(Method consumerMethod) { return MethodUtils.findSwaggerMethodName(consumerMethod); } /* * For producer, always generate swagger from code. */ public SwaggerProducer createProducer(Object producerInstance) { return createProducer(producerInstance, null); } /* * For producer, always generate swagger from code. */ public SwaggerProducer createProducer(Object producerInstance, Class schemaInterface) { Class producerCls = targetSwaggerClass(producerInstance, schemaInterface); OpenAPI swagger = SwaggerGenerator.generate(producerCls); Map, ContextArgumentMapperFactory> contextFactories = SPIServiceUtils .getOrLoadSortedService(ProducerContextArgumentMapperFactory.class) .stream() .collect(Collectors.toMap(ProducerContextArgumentMapperFactory::getContextClass, Function.identity())); ResponseMapperFactories producerResponseMapperFactories = new ResponseMapperFactories<>(ProducerResponseMapperFactory.class); SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); Map visibleProducerMethods = MethodUtils.findSwaggerMethodsMapOfOperationId(producerCls); SwaggerProducer producer = new SwaggerProducer(); producer.setSwagger(swagger); producer.setProducerCls(producerCls); producer.setProducerInstance(producerInstance); for (SwaggerOperation swaggerOperation : swaggerOperations.getOperations().values()) { String operationId = swaggerOperation.getOperationId(); // producer参数不一定等于swagger参数 Method producerMethod = visibleProducerMethods.getOrDefault(operationId, null); if (producerMethod == null) { // producer未实现契约,非法 String msg = String.format("operationId %s not exist in producer %s.", operationId, producerInstance.getClass().getName()); throw new IllegalStateException(msg); } ProducerArgumentsMapperCreator creator = new ProducerArgumentsMapperCreator( Json.mapper().getSerializationConfig(), contextFactories, producerCls, producerMethod, swaggerOperation); ProducerArgumentsMapper argsMapper = creator.createArgumentsMapper(); ProducerResponseMapper responseMapper = producerResponseMapperFactories.createResponseMapper( producerMethod.getGenericReturnType()); SwaggerProducerOperation op = new SwaggerProducerOperation(); op.setProducerClass(producerCls); op.setProducerInstance(producerInstance); op.setProducerMethod(producerMethod); op.setSwaggerOperation(swaggerOperation); op.setSwaggerParameterTypes(creator.getSwaggerParameterTypes()); op.setArgumentsMapper(argsMapper); op.setResponseMapper(responseMapper); producer.addOperation(op); } return producer; } private OpenAPI checkAndGenerateSwagger(Class swaggerClass, OpenAPI swagger) { if (swagger == null) { swagger = SwaggerGenerator.generate(swaggerClass); } return swagger; } private Class targetSwaggerClass(Object producerInstance, Class schemaInterface) { if (schemaInterface != null && !Object.class.equals(schemaInterface)) { return schemaInterface; } return BeanUtils.getImplClassFromBean(producerInstance); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import java.util.Collection; import java.util.HashMap; import java.util.Map; import io.swagger.v3.oas.models.OpenAPI; public class SwaggerProducer { private Class producerCls; private Object producerInstance; private OpenAPI swagger; // key is operationId private final Map opMap = new HashMap<>(); public Class getProducerCls() { return producerCls; } public void setProducerCls(Class producerCls) { this.producerCls = producerCls; } public Object getProducerInstance() { return producerInstance; } public void setProducerInstance(Object producerInstance) { this.producerInstance = producerInstance; } public OpenAPI getSwagger() { return swagger; } public void setSwagger(OpenAPI swagger) { this.swagger = swagger; } public void addOperation(SwaggerProducerOperation op) { opMap.put(op.getOperationId(), op); } public SwaggerProducerOperation findOperation(String operationId) { return opMap.get(operationId); } public Collection getAllOperations() { return opMap.values(); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; public class SwaggerProducerOperation { // 因为存在aop场景,所以,producerClass不一定等于producerInstance.getClass() private Class producerClass; private Object producerInstance; private Method producerMethod; private SwaggerOperation swaggerOperation; // swagger parameter types relate to producer // because features of @BeanParam/query wrapper/rpc mode parameter wrapper // types is not direct equals to producerMethod parameter types private Map swaggerParameterTypes; private ProducerArgumentsMapper argumentsMapper; private ProducerResponseMapper responseMapper; public String getOperationId() { return swaggerOperation.getOperationId(); } public Class getProducerClass() { return producerClass; } public void setProducerClass(Class producerClass) { this.producerClass = producerClass; } public Object getProducerInstance() { return producerInstance; } public void setProducerInstance(Object producerInstance) { this.producerInstance = producerInstance; } public Method getProducerMethod() { return producerMethod; } public void setProducerMethod(Method producerMethod) { this.producerMethod = producerMethod; } public SwaggerOperation getSwaggerOperation() { return swaggerOperation; } public void setSwaggerOperation(SwaggerOperation swaggerOperation) { this.swaggerOperation = swaggerOperation; } public Map getSwaggerParameterTypes() { return swaggerParameterTypes; } public void setSwaggerParameterTypes(Map swaggerParameterTypes) { this.swaggerParameterTypes = swaggerParameterTypes; } public ProducerArgumentsMapper getArgumentsMapper() { return argumentsMapper; } public void setArgumentsMapper(ProducerArgumentsMapper argumentsMapper) { this.argumentsMapper = argumentsMapper; } public ProducerResponseMapper getResponseMapper() { return responseMapper; } public void setResponseMapper(ProducerResponseMapper responseMapper) { this.responseMapper = responseMapper; } public Type getSwaggerParameterType(String name) { return this.swaggerParameterTypes.get(name); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/AsyncResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation; import jakarta.ws.rs.core.Response.StatusType; public interface AsyncResponse { void handle(Response response); default void success(StatusType status, Object result) { handle(Response.status(status).entity(result)); } default void success(Object result) { handle(Response.ok(result)); } default void consumerFail(Throwable e) { handle(Response.createConsumerFail(e)); } default void producerFail(Throwable e) { handle(Response.createProducerFail(e)); } default void fail(InvocationType type, Throwable e) { handle(Response.createFail(type, e)); } default void complete(Response resp) { handle(resp); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation; public enum InvocationType { CONSUMER, PROVIDER, EDGE } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/Response.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation; import java.util.List; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import io.vertx.core.MultiMap; /** * 用jaxrs的Response能表达所有概念 * 但是那个是abstract类,在不引入jersey之类的库的情况下,我们需要补充的功能太多 * * 所以,使用一个简单的归一化类 */ public class Response { private StatusType status; private MultiMap headers; // 失败场景中,result是Throwable private Object result; public boolean isSucceed() { return HttpStatus.isSuccess(status); } public boolean isFailed() { return !isSucceed(); } public int getStatusCode() { return status.getStatusCode(); } public String getReasonPhrase() { return status.getReasonPhrase(); } public StatusType getStatus() { return status; } public void setStatus(StatusType status) { this.status = status; } public String getHeader(String name) { if (headers == null) { return null; } return headers.get(name); } public List getHeaders(String name) { if (headers == null) { return null; } return headers.getAll(name); } public MultiMap getHeaders() { return headers; } public Response setHeaders(MultiMap headers) { this.headers = headers; return this; } public Response addHeader(String name, String value) { if (value == null) { return this; } if (headers == null) { headers = MultiMap.caseInsensitiveMultiMap(); } headers.add(name, value); return this; } public Response setHeader(String name, String value) { if (value == null) { return this; } if (headers == null) { headers = MultiMap.caseInsensitiveMultiMap(); } headers.set(name, value); return this; } @SuppressWarnings("unchecked") public T getResult() { return (T) result; } public void setResult(Object result) { this.result = result; } // 如果是成功,body即是result // 如果是失败,body可能是InvocationException,也可能是InvocationException中的errorData public static Response create(int statusCode, String reasonPhrase, Object result) { StatusType status = new HttpStatus(statusCode, reasonPhrase); return create(status, result); } public static Response create(StatusType status, Object result) { Response response = Response.status(status); if (response.isFailed()) { result = ExceptionFactory.create(status, result); } return response.entity(result); } // 有的场景下,需要返回非200的,其他2xx状态码,所以需要支持指定 public static Response createSuccess(StatusType status, Object result) { return Response.status(status).entity(result); } public static Response createSuccess(Object result) { return Response.status(Status.OK).entity(result); } public static Response createFail(InvocationException exception) { return Response.status(exception.getStatus()).entity(exception); } public static Response createFail(InvocationType invocationType, String errorMsg) { CommonExceptionData errorData = new CommonExceptionData(errorMsg); if (InvocationType.CONSUMER.equals(invocationType)) { return createFail(ExceptionFactory.createConsumerException(errorData)); } return createFail(ExceptionFactory.createProducerException(errorData)); } public static Response createFail(InvocationType invocationType, Throwable throwable) { if (InvocationType.CONSUMER.equals(invocationType)) { return createConsumerFail(throwable); } return createProducerFail(throwable); } public static Response createConsumerFail(Throwable throwable) { InvocationException exception = ExceptionFactory.convertConsumerException(throwable); return createFail(exception); } public static Response createConsumerFail(Throwable throwable, String message) { InvocationException exception = ExceptionFactory.convertConsumerException(throwable, message); return createFail(exception); } public static Response createProducerFail(Throwable throwable) { InvocationException exception = ExceptionFactory.convertProducerException(throwable); return createFail(exception); } // 兼容接口 public static Response consumerFailResp(Throwable e) { return createConsumerFail(e); } public static Response producerFailResp(Throwable e) { return createProducerFail(e); } public static Response providerFailResp(Throwable e) { return createProducerFail(e); } public static Response success(Object result, StatusType status) { return createSuccess(status, result); } public static Response succResp(Object result) { return createSuccess(result); } public static Response failResp(InvocationException e) { return createFail(e); } public static Response failResp(InvocationType invocationType, Throwable e) { return createFail(invocationType, e); } // 下面是jaxrs Response的一些常见用法,照搬过来 public Response entity(Object result) { setResult(result); return this; } public Response build() { return this; } public static Response status(StatusType status) { Response response = new Response(); response.setStatus(status); return response; } public static Response ok(Object result) { Response response = new Response(); response.setStatus(Status.OK); response.setResult(result); return response; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public class SwaggerInvocation extends InvocationContext { // 本实例是在consumer端,还是在provider端 protected InvocationType invocationType; protected InvocationContext parentContext; public SwaggerInvocation() { parentContext = ContextUtils.getInvocationContext(); if (parentContext != null) { addContext(parentContext.getContext()); addLocalContext(parentContext.getLocalContext()); } } public InvocationContext getParentContext() { return parentContext; } public InvocationType getInvocationType() { return invocationType; } public String getInvocationQualifiedName() { return invocationType.name(); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectParameterName; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import com.fasterxml.jackson.databind.SerializationConfig; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; /** *
 *   1.common
 *     context type parameter is not swagger parameter:
 *       InvocationContext
 *       HttpServletRequest
 *
 *   2.same version
 *     1) direct map (most scenes)
 *       interface method:
 *         class AddParam {
 *           int x;
 *           int y;
 *         }
 *         int add(InvocationContext context, AddParam param)
 *       swagger parameters:
 *         param
 *
 *       interface method:
 *         int add(int x, int y)
 *       swagger parameters:
 *         x, y
 *    2) swagger only one POJO parameter, extract all field to method parameters (POJO dev mode)
 *      interface method:
 *        int add(int x, int y)
 *      swagger parameters:
 *        param
 *    3) wrap some simple continuously swagger parameters to POJO (springmvc query parameters)
 *      interface method:
 *        int add(String name, AddParam param, Body body)
 *      swagger parameters:
 *        name, x, y, body
 *    4) wrap some simple and complex continuously swagger parameters to POJO  (JaxRS BeanParam)
 *      interface method:
 *        class BeanWrapper {
 *          int x;
 *          int y;
 *          Body body;
 *        }
 *        int add(String name, AddParam param, Body body)
 *      swagger parameters:
 *        name, x, y, body
 *  2. invoke old version
 *    interface method:
 *      int add(int x, int y, int z)
 *    swagger parameter:
 *      x, y
 *  3. invoke new version
 *    interface method:
 *      int add(int x, int y)
 *    swagger parameter:
 *      x, y, z
 * 
*/ @SuppressWarnings({"rawtypes", "unchecked"}) public abstract class AbstractArgumentsMapperCreator { protected boolean isSwaggerBodyField = false; protected SerializationConfig serializationConfig; // key is context class protected Map, ContextArgumentMapperFactory> contextFactories; // consumer or producer protected Method providerMethod; protected Class providerClass; protected SwaggerOperation swaggerOperation; protected List mappers = new ArrayList<>(); protected List swaggerParameters; protected RequestBody bodyParameter; // For pojo wrapped bodies only protected Map swaggerBodyProperties; protected Set processedSwaggerParameters; public AbstractArgumentsMapperCreator(SerializationConfig serializationConfig, Map, ContextArgumentMapperFactory> contextFactories, Class providerClass, Method providerMethod, SwaggerOperation swaggerOperation) { this.serializationConfig = serializationConfig; this.contextFactories = contextFactories; this.providerClass = providerClass; this.providerMethod = providerMethod; this.swaggerOperation = swaggerOperation; this.swaggerParameters = this.swaggerOperation.getOperation().getParameters(); this.bodyParameter = this.swaggerOperation.getOperation().getRequestBody(); this.swaggerBodyProperties = readSwaggerBodyProperties(); this.processedSwaggerParameters = new HashSet<>(); } private Map readSwaggerBodyProperties() { if (bodyParameter == null || bodyParameter.getContent() == null || bodyParameter.getContent().size() == 0) { return null; } // For pojo wrapped bodies only if (bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null || bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { return null; } Schema schema = bodyParameter.getContent().entrySet().iterator().next().getValue().getSchema(); if (schema != null && schema.get$ref() != null) { schema = SwaggerUtils.getSchema(swaggerOperation.getSwagger(), schema.get$ref()); } if (schema != null && schema.getProperties() != null) { return schema.getProperties(); } return null; } protected void doCreateArgumentsMapper() { java.lang.reflect.Parameter[] providerParameters = providerMethod.getParameters(); for (int providerParamIdx = 0; providerParamIdx < providerParameters.length; providerParamIdx++) { java.lang.reflect.Parameter providerParameter = providerParameters[providerParamIdx]; if (processContextParameter(providerParameter)) { continue; } String parameterName = collectParameterName(providerParameter); if (processKnownParameter(providerParamIdx, parameterName)) { processedSwaggerParameters.add(parameterName); continue; } if (processSwaggerBodyField(providerParamIdx, parameterName)) { processedSwaggerParameters.add(parameterName); isSwaggerBodyField = true; continue; } if (processBeanParameter(providerParamIdx, providerParameter)) { continue; } processUnknownParameter(providerParamIdx, providerParameter, parameterName); } // Process swagger parameters that not in method parameters if (swaggerParameters != null) { for (Parameter parameter : swaggerParameters) { if (!processedSwaggerParameters.contains(parameter.getName())) { processPendingSwaggerParameter(parameter); } } } if (bodyParameter != null) { processPendingBodyParameter(bodyParameter); } } /** * * @param providerParameter processing provider parameter * @return true means processed */ protected boolean processContextParameter(java.lang.reflect.Parameter providerParameter) { ContextArgumentMapperFactory contextFactory = contextFactories.get(providerParameter.getType()); if (contextFactory == null) { return false; } mappers.add(contextFactory .create(providerParameter.getName(), providerParameter.getName())); return true; } /** * Parameters has the same name in method and swagger. */ protected boolean processKnownParameter(int providerParamIdx, String invocationArgumentName) { if (!parameterNameExistsInSwagger(invocationArgumentName)) { return false; } ArgumentMapper mapper = createKnownParameterMapper(providerParamIdx, invocationArgumentName); mappers.add(mapper); return true; } protected boolean parameterNameExistsInSwagger(String parameterName) { if (this.swaggerParameters != null) { for (Parameter parameter : this.swaggerParameters) { if (parameterName.equals(parameter.getName())) { return true; } } } if (this.bodyParameter != null && this.bodyParameter.getContent() != null) { if (this.bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null && this.bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema() != null && this.bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties() != null) { return this.bodyParameter.getContent() .get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().get(parameterName) != null; } if (this.bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null && this.bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema() != null && this.bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema().getProperties() != null) { return this.bodyParameter.getContent() .get(SwaggerConst.FILE_MEDIA_TYPE).getSchema().getProperties().get(parameterName) != null; } } if (this.bodyParameter != null && this.bodyParameter.getExtensions() != null) { return parameterName.equals(this.bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME)); } return false; } protected abstract ArgumentMapper createKnownParameterMapper(int providerParamIdx, String parameterName); /** * Process POJO wrapped parameters, e.g. * method(int foo, int bar) * and Form parameters, e.g. * method(@FormParam("foo") int foo, @FormParam("bar") int bar) */ protected boolean processSwaggerBodyField(int providerParamIdx, String parameterName) { if (swaggerBodyProperties == null || swaggerBodyProperties.get(parameterName) == null) { return false; } ArgumentMapper mapper = createSwaggerBodyFieldMapper(providerParamIdx, parameterName); mappers.add(mapper); return true; } protected abstract ArgumentMapper createSwaggerBodyFieldMapper(int providerParamIdx, String parameterName); /** * Bean parameters, e.g. * * method(QueryModels queries) * * where swagger should be: * - in: query * name: foo * - in: query * name: bar */ protected abstract boolean processBeanParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter); protected abstract void processUnknownParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter, String parameterName); /** * Process parameters that in swagger but not in method. */ protected abstract void processPendingSwaggerParameter(Parameter parameter); /** * Process body parameter that in swagger but not in method. */ protected abstract void processPendingBodyParameter(RequestBody parameter); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public interface ArgumentMapper { void swaggerArgumentToInvocationArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments); void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public interface ArgumentsMapper { default Map swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, Map swaggerArguments) { throw new IllegalStateException("not implemented."); } /** *
   * for scenes that consumer arguments not same to contract arguments, eg:
   * 1.consumer: int add(QueryWrapper query)
   *             class QueryWrapper {
   *               public int x;
   *               public int y;
   *             }
   *   contract: int add(int x, int y)
   *
   * 2.consumer: int add(InvocationContext context, int x, int y);
   *   contract: int add(int x, int y)
   *
   * 3.consumer: int add(int x, int y)
   *   contract: int add(BodyRequest body)
   *             class BodyRequest {
   *               public int x;
   *               public int y;
   *             }
   *
   * notice:
   *   no convert logic when map arguments
   *   map arguments by name, DO NOT use duplicated contract argument names
   * 
* */ default Map invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map invocationArguments) { throw new IllegalStateException("not implemented."); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments; public interface ContextArgumentMapperFactory { Class getContextClass(); ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperCommon.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; /** * map consumer arguments to swagger arguments */ public class ArgumentsMapperCommon implements ArgumentsMapper { private final List mappers; public ArgumentsMapperCommon(List mappers) { this.mappers = mappers; } @Override public Map invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map invocationArguments) { Map swaggerParameters = new HashMap<>(invocationArguments.size()); for (ArgumentMapper argMapper : mappers) { argMapper.invocationArgumentToSwaggerArguments(swaggerInvocation, swaggerParameters, invocationArguments); } return swaggerParameters; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperDirectReuse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; public class ArgumentsMapperDirectReuse implements ArgumentsMapper { @Override public Map invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map invocationArguments) { return invocationArguments; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; public abstract class ConsumerArgumentMapper implements ArgumentMapper { public void swaggerArgumentToInvocationArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments) { throw new IllegalStateException("not expected"); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public final class ConsumerArgumentSame extends ConsumerArgumentMapper { private final String invocationArgumentName; private final String swaggerArgumentName; public ConsumerArgumentSame(String invocationArgumentName, String swaggerArgumentName) { this.invocationArgumentName = invocationArgumentName; this.swaggerArgumentName = swaggerArgumentName; } public boolean isSameMapping() { return this.invocationArgumentName.equals(this.swaggerArgumentName); } @Override public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments) { swaggerArguments.put(swaggerArgumentName, invocationArguments.get(invocationArgumentName)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.LinkedHashMap; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; /** *
 * Typical scene of transparent RPC
 * all parameters of consumer method wrapped to a bean in contract
 * 
*/ public final class ConsumerArgumentToBodyField extends ConsumerArgumentMapper { private final String invocationArgumentName; private final String parameterName; private final String swaggerArgumentName; public ConsumerArgumentToBodyField(String invocationArgumentName, String swaggerArgumentName, String parameterName) { this.invocationArgumentName = invocationArgumentName; this.parameterName = parameterName; this.swaggerArgumentName = swaggerArgumentName; } @SuppressWarnings("unchecked") @Override public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments) { Object consumerArgument = invocationArguments.get(invocationArgumentName); swaggerArguments.computeIfAbsent(swaggerArgumentName, k -> new LinkedHashMap()); if (consumerArgument != null) { ((Map) swaggerArguments.get(swaggerArgumentName)).put(parameterName, consumerArgument); } } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectParameterName; import java.lang.reflect.Method; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.arguments.AbstractArgumentsMapperCreator; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; public class ConsumerArgumentsMapperCreator extends AbstractArgumentsMapperCreator { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerArgumentsMapperCreator.class); private int unknownConsumerParams = 0; public ConsumerArgumentsMapperCreator(SerializationConfig serializationConfig, Map, ContextArgumentMapperFactory> contextFactories, Class consumerClass, Method consumerMethod, SwaggerOperation swaggerOperation) { super(serializationConfig, contextFactories, consumerClass, consumerMethod, swaggerOperation); } private boolean isAllSameMapper() { for (ArgumentMapper mapper : mappers) { if (mapper instanceof ConsumerArgumentSame && ((ConsumerArgumentSame) mapper).isSameMapping()) { continue; } return false; } return true; } public ArgumentsMapper createArgumentsMapper() { doCreateArgumentsMapper(); // if all mappers are SameMapper, then no need any mapper if (unknownConsumerParams == 0 && mappers.size() == swaggerOperation.parameterCount() && isAllSameMapper()) { return new ArgumentsMapperDirectReuse(); } return new ArgumentsMapperCommon(mappers); } @Override protected void processUnknownParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter, String parameterName) { LOGGER.warn("Consumer parameter({}) is not exist in contract, method={}:{}.", parameterName, providerMethod.getDeclaringClass().getName(), providerMethod.getName()); unknownConsumerParams++; } @Override protected void processPendingSwaggerParameter(Parameter parameter) { } @Override protected void processPendingBodyParameter(RequestBody parameter) { } @Override protected ArgumentMapper createKnownParameterMapper(int providerParamIdx, String invocationArgumentName) { return new ConsumerArgumentSame(this.providerMethod.getParameters()[providerParamIdx].getName(), invocationArgumentName); } @Override protected ArgumentMapper createSwaggerBodyFieldMapper(int consumerParamIdx, String parameterName) { return new ConsumerArgumentToBodyField(this.providerMethod.getParameters()[consumerParamIdx].getName(), (String) this.bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME), parameterName); } @Override protected boolean processBeanParameter(int consumerParamIdx, java.lang.reflect.Parameter consumerParameter) { JavaType consumerType = TypeFactory.defaultInstance().constructType(consumerParameter.getParameterizedType()); if (!SwaggerUtils.isBean(consumerType)) { return false; } boolean result = false; ConsumerBeanParamMapper mapper = new ConsumerBeanParamMapper( this.providerMethod.getParameters()[consumerParamIdx].getName()); for (BeanPropertyDefinition propertyDefinition : serializationConfig.introspect(consumerType).findProperties()) { String parameterName = collectParameterName(providerMethod, propertyDefinition); if (!parameterNameExistsInSwagger(parameterName)) { continue; } mapper.addField(parameterName, LambdaMetafactoryUtils.createObjectGetter(propertyDefinition)); processedSwaggerParameters.add(parameterName); result = true; } mappers.add(mapper); return result; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerBeanParamMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.bean.Getter; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; /** *
 * consumer: void add(QueryWrapper query)
 *           class QueryWrapper {
 *             int x;
 *             int y;
 *           }
 * contract; void add(int x, int y)
 * 
*/ public final class ConsumerBeanParamMapper extends ConsumerArgumentMapper { private static class FieldMeta { String swaggerArgumentName; Getter getter; public FieldMeta(String swaggerArgumentName, Getter getter) { this.swaggerArgumentName = swaggerArgumentName; this.getter = getter; } } private final String invocationArgumentName; private final List fields = new ArrayList<>(); public ConsumerBeanParamMapper(String invocationArgumentName) { this.invocationArgumentName = invocationArgumentName; } public void addField(String invocationArgumentName, Getter getter) { fields.add(new FieldMeta(invocationArgumentName, getter)); } @Override public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments) { Object consumerArgument = invocationArguments.get(invocationArgumentName); if (consumerArgument == null) { return; } for (FieldMeta fieldMeta : fields) { swaggerArguments.put(fieldMeta.swaggerArgumentName, fieldMeta.getter.get(consumerArgument)); } } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerContextArgumentMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; public interface ConsumerContextArgumentMapperFactory extends ContextArgumentMapperFactory { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public class ConsumerInvocationContextMapper extends ConsumerArgumentMapper { protected String invocationArgumentName; public ConsumerInvocationContextMapper(String invocationArgumentName) { this.invocationArgumentName = invocationArgumentName; } @Override public void invocationArgumentToSwaggerArguments(SwaggerInvocation invocation, Map swaggerArguments, Map invocationArguments) { InvocationContext context = (InvocationContext) invocationArguments.get(invocationArgumentName); invocation.addContext(context.getContext()); invocation.addLocalContext(context.getLocalContext()); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public class ConsumerInvocationContextMapperFactory implements ConsumerContextArgumentMapperFactory { @Override public Class getContextClass() { return InvocationContext.class; } @Override public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { return new ConsumerInvocationContextMapper(invocationArgumentName); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public abstract class AbstractProducerContextArgMapper extends ProducerArgumentMapper { protected String invocationArgumentName; protected String swaggerArgumentName; public AbstractProducerContextArgMapper(String invocationArgumentName, String swaggerArgumentName) { this.invocationArgumentName = invocationArgumentName; this.swaggerArgumentName = swaggerArgumentName; } @Override public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, Map swaggerArguments, Map invocationArguments) { Object producerArg = createContextArg(invocation); invocationArguments.put(this.invocationArgumentName, producerArg); } public abstract Object createContextArg(SwaggerInvocation invocation); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; public abstract class ProducerArgumentMapper implements ArgumentMapper { public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, Map invocationArguments) { throw new IllegalStateException("not expected"); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public class ProducerArgumentSame extends ProducerArgumentMapper { protected String invocationArgumentName; protected String swaggerArgumentName; public ProducerArgumentSame(String invocationArgumentName, String swaggerArgumentName) { this.invocationArgumentName = invocationArgumentName; this.swaggerArgumentName = swaggerArgumentName; } @Override public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, Map swaggerArguments, Map invocationArguments) { invocationArguments.put(invocationArgumentName, swaggerArguments.get(swaggerArgumentName)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; /** * map swagger arguments to producer arguments */ public class ProducerArgumentsMapper implements ArgumentsMapper { private final List producerArgMapperList; public ProducerArgumentsMapper(List producerArgMapperList) { this.producerArgMapperList = producerArgMapperList; } @Override public Map swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, Map swaggerArguments) { Map invocationArguments = new HashMap<>(swaggerArguments.size()); for (ArgumentMapper argMapper : producerArgMapperList) { argMapper.swaggerArgumentToInvocationArguments(invocation, swaggerArguments, invocationArguments); } return invocationArguments; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectParameterName; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.arguments.AbstractArgumentsMapperCreator; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.type.TypeFactory; import com.google.common.reflect.TypeToken; import io.swagger.v3.oas.models.parameters.RequestBody; @SuppressWarnings("unchecked") public class ProducerArgumentsMapperCreator extends AbstractArgumentsMapperCreator { // swagger parameter types relate to producer // because features of @BeanParam/query, and rpc mode parameter wrapper // types is not always equals to producerMethod parameter types directly private final Map swaggerParameterTypes; public ProducerArgumentsMapperCreator(SerializationConfig serializationConfig, Map, ContextArgumentMapperFactory> contextFactories, Class producerClass, Method producerMethod, SwaggerOperation swaggerOperation) { super(serializationConfig, contextFactories, producerClass, producerMethod, swaggerOperation); swaggerParameterTypes = new HashMap<>(); } public Map getSwaggerParameterTypes() { return swaggerParameterTypes; } public ProducerArgumentsMapper createArgumentsMapper() { doCreateArgumentsMapper(); return new ProducerArgumentsMapper(mappers); } @Override protected void processUnknownParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter, String parameterName) { throw new IllegalStateException(String .format("failed to find producer parameter in contract, method=%s:%s, parameter name=%s.", providerMethod.getDeclaringClass().getSimpleName(), providerMethod.getName(), parameterName)); } @Override protected void processPendingSwaggerParameter(io.swagger.v3.oas.models.parameters.Parameter parameter) { swaggerParameterTypes.put(parameter.getName(), Object.class); } @Override protected void processPendingBodyParameter(RequestBody bodyParameter) { if (bodyParameter.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE) != null && !processedSwaggerParameters.contains( (String) bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME))) { swaggerParameterTypes.put((String) bodyParameter .getExtensions().get(SwaggerConst.EXT_BODY_NAME), Object.class); } if (bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().forEach((k, v) -> { if (!processedSwaggerParameters.contains((String) k)) { swaggerParameterTypes.put((String) k, Object.class); } }); } } @Override protected ArgumentMapper createKnownParameterMapper(int providerParamIdx, String invocationArgumentName) { Type providerType = TypeToken.of(providerClass) .resolveType(providerMethod.getGenericParameterTypes()[providerParamIdx]) .getType(); swaggerParameterTypes .put(invocationArgumentName, providerType); return new ProducerArgumentSame(providerMethod.getParameters()[providerParamIdx].getName(), invocationArgumentName); } @Override protected ArgumentMapper createSwaggerBodyFieldMapper(int producerParamIdx, String parameterName) { String swaggerArgumentName = (String) this.bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME); swaggerParameterTypes.put(swaggerArgumentName, Object.class); Type parameterType = TypeToken.of(providerClass) .resolveType(providerMethod.getGenericParameterTypes()[producerParamIdx]) .getType(); return new SwaggerBodyFieldToProducerArgument(providerMethod.getParameters()[producerParamIdx].getName(), swaggerArgumentName, parameterName, parameterType); } @Override protected boolean processBeanParameter(int producerParamIdx, Parameter producerParameter) { JavaType providerType = TypeFactory.defaultInstance().constructType(producerParameter.getParameterizedType()); if (!SwaggerUtils.isBean(providerType)) { return false; } ProducerBeanParamMapper mapper = new ProducerBeanParamMapper( providerMethod.getParameters()[producerParamIdx].getName(), producerParameter.getType()); boolean result = false; for (BeanPropertyDefinition propertyDefinition : serializationConfig.introspect(providerType) .findProperties()) { String parameterName = collectParameterName(providerMethod, propertyDefinition); if (!parameterNameExistsInSwagger(parameterName)) { continue; } swaggerParameterTypes.put(parameterName, propertyDefinition.getPrimaryType()); mapper.addField(parameterName, LambdaMetafactoryUtils.createObjectSetter(propertyDefinition)); processedSwaggerParameters.add(parameterName); result = true; } mappers.add(mapper); return result; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.bean.Setter; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public class ProducerBeanParamMapper extends ProducerArgumentMapper { private static class FieldMeta { String swaggerParameterName; Setter setter; public FieldMeta(String swaggerParameterName, Setter setter) { this.swaggerParameterName = swaggerParameterName; this.setter = setter; } } protected String invocationArgumentName; private final Class producerParamType; private final List fields = new ArrayList<>(); public ProducerBeanParamMapper(String invocationArgumentName, Class producerParamType) { this.invocationArgumentName = invocationArgumentName; this.producerParamType = producerParamType; } public void addField(String swaggerParameterName, Setter setter) { fields.add(new FieldMeta(swaggerParameterName, setter)); } @Override public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, Map swaggerArguments, Map invocationArguments) { try { Object paramInstance = producerParamType.getDeclaredConstructor().newInstance(); invocationArguments.put(invocationArgumentName, paramInstance); for (FieldMeta fieldMeta : fields) { Object value = swaggerArguments.get(fieldMeta.swaggerParameterName); if (value != null) { // can not set primitive data fieldMeta.setter.set(paramInstance, value); } } } catch (Throwable e) { throw new IllegalStateException("failed to map bean param.", e); } } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerContextArgumentMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; public interface ProducerContextArgumentMapperFactory extends ContextArgumentMapperFactory { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; public class ProducerInvocationContextMapper extends AbstractProducerContextArgMapper { public ProducerInvocationContextMapper(String invocationArgumentName, String swaggerArgumentName) { super(invocationArgumentName, swaggerArgumentName); } @Override public Object createContextArg(SwaggerInvocation invocation) { return invocation; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public class ProducerInvocationContextMapperFactory implements ProducerContextArgumentMapperFactory { @Override public Class getContextClass() { return InvocationContext.class; } @Override public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { return new ProducerInvocationContextMapper(invocationArgumentName, swaggerArgumentName); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SwaggerBodyFieldToProducerArgument.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.lang.reflect.Type; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; public class SwaggerBodyFieldToProducerArgument extends ProducerArgumentMapper { public static ObjectMapper mapper = JsonUtils.OBJ_MAPPER; private final String invocationArgumentName; private final String parameterName; private final JavaType producerParamType; private final String swaggerArgumentName; public SwaggerBodyFieldToProducerArgument(String invocationArgumentName, String swaggerArgumentName, String parameterName, Type producerParamType) { this.invocationArgumentName = invocationArgumentName; this.parameterName = parameterName; this.producerParamType = TypeFactory.defaultInstance().constructType(producerParamType); this.swaggerArgumentName = swaggerArgumentName; } @Override @SuppressWarnings("unchecked") public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, Map swaggerArguments, Map invocationArguments) { Map body = (Map) swaggerArguments.get(swaggerArgumentName); invocationArguments.put(invocationArgumentName, body == null ? null : mapper.convertValue(body.get(parameterName), producerParamType)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/ContextUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; import java.util.concurrent.CompletableFuture; /** * 传递调用过程的上下文数据 */ public final class ContextUtils { private ContextUtils() { } private static final ThreadLocal contextMgr = new ThreadLocal<>(); public static InvocationContext getInvocationContext() { return contextMgr.get(); } public static InvocationContext getAndRemoveInvocationContext() { InvocationContext context = contextMgr.get(); if (context != null) { contextMgr.remove(); } return context; } public static void setInvocationContext(InvocationContext invocationContext) { contextMgr.set(invocationContext); } public static void removeInvocationContext() { contextMgr.remove(); } /** * * @param future must be InvocationContextCompletableFuture, that is returned from consumer api * @return */ public static InvocationContext getFromCompletableFuture(CompletableFuture future) { if (future instanceof InvocationContextCompletableFuture) { return ((InvocationContextCompletableFuture) future).getContext(); } return null; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatus.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; public class HttpStatus extends org.apache.servicecomb.foundation.common.http.HttpStatus { public HttpStatus(int statusCode, String reasonPhrase) { super(statusCode, reasonPhrase); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatusManager.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; public class HttpStatusManager extends org.apache.servicecomb.foundation.common.http.HttpStatusManager { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; import java.util.HashMap; import java.util.Map; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; /** * InvocationContext is used to pass data between microservices or in microservice different layer. */ public class InvocationContext { private static final HttpStatusManager statusMgr = new HttpStatusManager(); protected StatusType httpStatus; protected final Map context = new HashMap<>(); protected final Map localContext = new HashMap<>(); protected TransportContext transportContext; public InvocationContext() { httpStatus = Status.OK; } public Map getContext() { return context; } public void setContext(Map context) { this.context.clear(); this.addContext(context); } public void addContext(String key, String value) { context.put(key, value); } public String getContext(String key) { return context.get(key); } public void addContext(InvocationContext otherContext) { addContext(otherContext.getContext()); } public void addContext(Map otherContext) { if (otherContext == null) { return; } context.putAll(otherContext); } public void mergeContext(InvocationContext otherContext) { mergeContext(otherContext.getContext()); } public void mergeContext(Map otherContext) { if (otherContext == null) { return; } context.putAll(otherContext); } public Map getLocalContext() { return localContext; } public void setLocalContext(Map localContext) { this.localContext.clear(); this.addLocalContext(localContext); } public void addLocalContext(String key, Object value) { localContext.put(key, value); } @SuppressWarnings("unchecked") public T getLocalContext(String key) { return (T) localContext.get(key); } public void addLocalContext(Map otherContext) { if (otherContext == null) { return; } localContext.putAll(otherContext); } public StatusType getStatus() { return httpStatus; } public void setStatus(StatusType status) { this.httpStatus = status; } public void setStatus(int statusCode, String reason) { httpStatus = new HttpStatus(statusCode, reason); } public void setStatus(int statusCode) { this.httpStatus = statusMgr.getOrCreateByStatusCode(statusCode); } @SuppressWarnings("unchecked") public T getTransportContext() { return (T) transportContext; } public void setTransportContext(TransportContext transportContext) { this.transportContext = transportContext; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContextCompletableFuture.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; import java.util.concurrent.CompletableFuture; public class InvocationContextCompletableFuture extends CompletableFuture { private final InvocationContext context; public InvocationContextCompletableFuture(InvocationContext context) { this.context = context; } public InvocationContext getContext() { return context; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/TransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; /** * currently, only for server side */ public interface TransportContext { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/VertxTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; import io.vertx.core.Context; public interface VertxTransportContext extends TransportContext { Context getVertxContext(); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/Converter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter; import java.lang.reflect.Type; public interface Converter { Type getSrcType(); Type getTargetType(); Object convert(Object value); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/BytesToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.io.ByteArrayInputStream; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.swagger.invocation.converter.Converter; public class BytesToPartConverter implements Converter { @Override public Type getSrcType() { return byte[].class; } @Override public Type getTargetType() { return Part.class; } @Override public Object convert(Object value) { // not set name, because not easy to get parameter name in this place // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name return new InputStreamPart(null, new ByteArrayInputStream((byte[]) value)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/FileToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.io.File; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.FilePart; import org.apache.servicecomb.swagger.invocation.converter.Converter; public class FileToPartConverter implements Converter { @Override public Type getSrcType() { return File.class; } @Override public Type getTargetType() { return Part.class; } @Override public Object convert(Object value) { // not set name, because not easy to get parameter name in this place // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name return new FilePart(null, (File) value); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/InputStreamToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.io.InputStream; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.swagger.invocation.converter.Converter; public class InputStreamToPartConverter implements Converter { @Override public Type getSrcType() { return InputStream.class; } @Override public Type getTargetType() { return Part.class; } @Override public Object convert(Object value) { // not set name, because not easy to get parameter name in this place // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name return new InputStreamPart(null, (InputStream) value); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.lang.reflect.Type; import java.util.List; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.swagger.invocation.converter.Converter; import jakarta.servlet.http.Part; public class PartListToPartArrayConverter implements Converter { @Override public Type getSrcType() { return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Type getTargetType() { return Part[].class; } @Override public Object convert(Object value) { if (value == null) { return null; } @SuppressWarnings("unchecked") List partList = (List) value; return partList.toArray(new Part[0]); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.lang.reflect.Type; import java.util.List; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.swagger.invocation.converter.Converter; import jakarta.servlet.http.Part; public class PartListToPartListConverter implements Converter { @Override public Type getSrcType() { return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Type getTargetType() { return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Object convert(Object value) { return value; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.apache.servicecomb.swagger.invocation.converter.Converter; public class PartToPartConverter implements Converter { @Override public Type getSrcType() { return Part.class; } @Override public Type getTargetType() { return Part.class; } @Override public Object convert(Object value) { return value; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/ResourceToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.ResourcePart; import org.apache.servicecomb.swagger.invocation.converter.Converter; import org.springframework.core.io.Resource; public class ResourceToPartConverter implements Converter { @Override public Type getSrcType() { return Resource.class; } @Override public Type getTargetType() { return Part.class; } @Override public Object convert(Object value) { // not set name, because not easy to get parameter name in this place // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name return new ResourcePart(null, (Resource) value); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionData.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.exception; import org.apache.servicecomb.foundation.common.DynamicObject; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; /** * 将普通异常转换为InvocationException时,保存message信息 */ public class CommonExceptionData extends DynamicObject { @JsonInclude(Include.NON_NULL) private String code; private String message; public CommonExceptionData() { } public CommonExceptionData(String message) { this.message = message; } public CommonExceptionData(String code, String message) { this.code = code; this.message = message; } public String getCode() { return code; } public CommonExceptionData setCode(String code) { this.code = code; return this; } public String getMessage() { return message; } public CommonExceptionData setMessage(String message) { this.message = message; return this; } @Override public String toString() { if (code == null) { return "CommonExceptionData [message=" + message + "]"; } return "CommonExceptionData{" + "code='" + code + '\'' + ", message='" + message + '\'' + ", dynamic=" + dynamic + '}'; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.exception; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import jakarta.ws.rs.core.Response.StatusType; public final class ExceptionFactory { // cse内置的错误 // 考虑到springmvc不支持自定义http错误码,所以只能使用“标准”错误码 // 510是ws.rs中未定义,在springmvc中定义为not extended // 在我们的流程中不可能用到这个错误定义,所以将之转义为cse的provider内部错误 // private static final int PROVIDER_INNER_STATUS_CODE = 510; public static final int PRODUCER_INNER_STATUS_CODE = 590; public static final String PRODUCER_INNER_REASON_PHRASE = "Unexpected producer error, please check logs for details"; public static final StatusType PRODUCER_INNER_STATUS = new HttpStatus(PRODUCER_INNER_STATUS_CODE, PRODUCER_INNER_REASON_PHRASE); // 420是ws.rs中未定义,在springmvc中定义为Method Failure // 在我们的流程中不可能用到这个错误定义,所以将之转义为cse的consumer内部错误 // private static final int CONSUMER_INNER_STATUS_CODE = 420; public static final int CONSUMER_INNER_STATUS_CODE = 490; public static final String CONSUMER_INNER_REASON_PHRASE = "Unexpected consumer error, please check logs for details"; public static final StatusType CONSUMER_INNER_STATUS = new HttpStatus(CONSUMER_INNER_STATUS_CODE, CONSUMER_INNER_REASON_PHRASE); private ExceptionFactory() { } public static InvocationException create(StatusType status, Object exceptionOrErrorData) { if (exceptionOrErrorData instanceof InvocationException) { return (InvocationException) exceptionOrErrorData; } return doCreate(status, exceptionOrErrorData); } public static InvocationException createConsumerException(Object errorData) { return create(CONSUMER_INNER_STATUS, errorData); } public static InvocationException createProducerException(Object errorData) { return create(PRODUCER_INNER_STATUS, errorData); } private static InvocationException doCreate(StatusType status, Object errorData) { return new InvocationException(status, errorData); } private static InvocationException doCreate(int statusCode, String reasonPhrase, CommonExceptionData data, Throwable e) { return new InvocationException(statusCode, reasonPhrase, data, e); } public static InvocationException convertConsumerException(Throwable e) { return convertException(CONSUMER_INNER_STATUS_CODE, CONSUMER_INNER_REASON_PHRASE, e, CONSUMER_INNER_REASON_PHRASE); } public static InvocationException convertConsumerException(Throwable e, String errorMsg) { return convertException(CONSUMER_INNER_STATUS_CODE, CONSUMER_INNER_REASON_PHRASE, e, errorMsg); } public static InvocationException convertProducerException(Throwable e) { return convertException(PRODUCER_INNER_STATUS_CODE, PRODUCER_INNER_REASON_PHRASE, e, PRODUCER_INNER_REASON_PHRASE); } public static InvocationException convertProducerException(Throwable e, String errorMsg) { return convertException(PRODUCER_INNER_STATUS_CODE, PRODUCER_INNER_REASON_PHRASE, e, errorMsg); } // 如果e中取不出可以直接返回的InvocationException // 则需要创建新的InvocationException实例,此时不允许使用e.getMessage,因为可能会涉及关键信息被传给远端 // 新创建的InvocationException,会使用errorMsg来构建CommonExceptionData private static InvocationException convertException(int statusCode, String reasonPhrase, Throwable e, String errorMsg) { e = unwrap(e); if (e instanceof InvocationException) { return (InvocationException) e; } CommonExceptionData data = new CommonExceptionData(errorMsg); return doCreate(statusCode, reasonPhrase, data, e); } public static Throwable unwrapIncludeInvocationException(Throwable throwable) { throwable = unwrap(throwable); if (throwable instanceof InvocationException) { throwable = throwable.getCause(); } return throwable; } @SuppressWarnings("unchecked") public static T unwrap(Throwable throwable) { if (throwable instanceof InvocationTargetException) { throwable = ((InvocationTargetException) throwable).getTargetException(); } if (throwable instanceof CompletionException) { throwable = throwable.getCause(); } if (throwable instanceof ExecutionException) { throwable = throwable.getCause(); } return (T) throwable; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.exception; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; /** * 业务在provider端,想返回在swagger中定义好的错误 * 通过抛出本类型的异常来实现 * 如果不是本类型的异常,则强制认为是PRODUCER_INNER_STATUS_CODE错误 */ public class InvocationException extends RuntimeException { private static final long serialVersionUID = 8027482777502649656L; /** * http header中的statusCode * 不直接使用Status类型,是为了支持业务自定义code */ private final StatusType status; private final Object errorData; public InvocationException(StatusType status, Object errorData) { this.status = status; this.errorData = errorData; } public InvocationException(StatusType status, String msg) { this(status, new CommonExceptionData(msg)); } public InvocationException(int statusCode, String reasonPhrase, Object errorData, Throwable cause) { this(new HttpStatus(statusCode, reasonPhrase), errorData, cause); } public InvocationException(int statusCode, String reasonPhrase, Object errorData) { this(new HttpStatus(statusCode, reasonPhrase), errorData); } public InvocationException(StatusType status, Object errorData, Throwable cause) { super(cause); this.status = status; this.errorData = errorData; } public InvocationException(StatusType status, String code, String msg) { this(status, new CommonExceptionData(code, msg)); } public InvocationException(StatusType status, String code, String msg, Throwable cause) { this(status, new CommonExceptionData(code, msg), cause); } public StatusType getStatus() { return status; } public int getStatusCode() { return status.getStatusCode(); } public String getReasonPhrase() { return status.getReasonPhrase(); } public Object getErrorData() { return errorData; } @SuppressWarnings("unchecked") public T getError() { return (T) errorData; } @Override public String getMessage() { return this.toString(); } @Override public String toString() { return "InvocationException: code=" + getStatusCode() + ";msg=" + getErrorData(); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.generator; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public class InvocationContextProcessor implements SwaggerContextRegister { @Override public Type getContextType() { return InvocationContext.class; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/ScbResponseProcessor.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.generator; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; import org.apache.servicecomb.swagger.invocation.Response; public class ScbResponseProcessor extends DefaultResponseTypeProcessor { @Override public Class getProcessType() { return Response.class; } @Override public Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType) { return null; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response; import java.lang.reflect.Type; import java.util.List; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; public class ResponseMapperFactories { private final List> factories; @SuppressWarnings("unchecked") public ResponseMapperFactories(Class> factoryCls) { factories = (List>) SPIServiceUtils.getSortedService(factoryCls); } public MAPPER createResponseMapper(Type providerType) { for (ResponseMapperFactory factory : factories) { if (!factory.isMatch(providerType)) { continue; } return factory.createResponseMapper(this, providerType); } throw new IllegalStateException( String.format("can not find response mapper for %s, this should never happened.", providerType.getTypeName())); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response; import java.lang.reflect.Type; public interface ResponseMapperFactory { default int getOrder() { return 0; } boolean isMatch(Type providerType); MAPPER createResponseMapper(ResponseMapperFactories factories, Type providerType); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMetaMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response; import java.util.Map; import com.fasterxml.jackson.databind.JavaType; public interface ResponseMetaMapper { default int getOrder() { return 0; } Map getMapper(); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponsesMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.converter.ConverterMgr; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.SimpleType; import io.swagger.v3.core.util.ReflectionUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.responses.ApiResponse; import jakarta.ws.rs.core.Response.Status; /** *
 * two Scenes:
 * 1.consumer interface + swagger
 *   interface declare success response type
 *   and can declare exceptions response type by annotations
 *   consumer interface meta never changed and has high priority
 *
 *   so, merge them to be one ResponsesMeta
 *
 * 2.restTemplate + swagger
 *   can only declare success response type
 *   and not stable
 *
 *   so, will wrap swagger meta
 *
 *   note:
 *   old version support: List<User> users = restTemplate.postForObject(...., List.class)
 *     in fact, in this time, type is determined by swagger meta
 *   new version:
 *     1) if request response type is List/Set/Map, and there is element type defined, then use swagger type,
 *     2) other times use request response type
 *     3) compare to old version, add support of ParameterizedTypeReference
 * 
*/ public class ResponsesMeta { private static final JavaType COMMON_EXCEPTION_JAVA_TYPE = SimpleType.constructUnsafe(CommonExceptionData.class); private static final JavaType OBJECT_JAVA_TYPE = SimpleType.constructUnsafe(Object.class); private static final ResponseMetaMapper GLOBAL_DEFAULT_MAPPER = SPIServiceUtils .getPriorityHighestService(ResponseMetaMapper.class); private final Map responseMap = new HashMap<>(); private JavaType defaultResponse; public void init(OpenAPI swagger, Operation operation) { if (responseMap.isEmpty()) { responseMap.put(Status.OK.getStatusCode(), OBJECT_JAVA_TYPE); initGlobalDefaultMapper(); } for (Entry entry : operation.getResponses().entrySet()) { if (entry.getValue().getContent() == null || entry.getValue().getContent().size() == 0) { continue; } String mediaType = entry.getValue().getContent().keySet().iterator().next(); JavaType javaType = ConverterMgr.findJavaType(swagger, SwaggerUtils.getSchema(swagger, entry.getValue().getContent().get(mediaType).getSchema())); if ("default".equals(entry.getKey())) { defaultResponse = javaType; continue; } Integer statusCode = Integer.parseInt(entry.getKey()); JavaType existing = responseMap.get(statusCode); if (existing == null || !ReflectionUtils.isVoid(javaType)) { responseMap.put(statusCode, javaType); } } responseMap.putIfAbsent(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, COMMON_EXCEPTION_JAVA_TYPE); responseMap.putIfAbsent(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, COMMON_EXCEPTION_JAVA_TYPE); responseMap.putIfAbsent(Status.TOO_MANY_REQUESTS.getStatusCode(), COMMON_EXCEPTION_JAVA_TYPE); responseMap.putIfAbsent(Status.REQUEST_TIMEOUT.getStatusCode(), COMMON_EXCEPTION_JAVA_TYPE); responseMap.putIfAbsent(Status.SERVICE_UNAVAILABLE.getStatusCode(), COMMON_EXCEPTION_JAVA_TYPE); if (defaultResponse == null) { // swagger中没有定义default,加上default专用于处理exception defaultResponse = OBJECT_JAVA_TYPE; } } public void cloneTo(ResponsesMeta target) { target.defaultResponse = defaultResponse; target.responseMap.putAll(responseMap); } protected void initGlobalDefaultMapper() { if (GLOBAL_DEFAULT_MAPPER != null) { Map mappers = GLOBAL_DEFAULT_MAPPER.getMapper(); if (mappers != null) { responseMap.putAll(mappers); } } } public JavaType findResponseType(int statusCode) { JavaType responseType = responseMap.get(statusCode); if (responseType == null) { if (HttpStatus.isSuccess(statusCode)) { return responseMap.get(Status.OK.getStatusCode()); } return defaultResponse; } return responseType; } public void setResponseType(int statusCode, JavaType javaType) { this.responseMap.put(statusCode, javaType); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CompletableFutureConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override public boolean isMatch(Type consumerType) { if (!ParameterizedType.class.isAssignableFrom(consumerType.getClass())) { return false; } return ((ParameterizedType) consumerType).getRawType().equals(CompletableFuture.class); } @Override public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; return factories.createResponseMapper(realConsumerType); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import org.apache.servicecomb.swagger.invocation.Response; public interface ConsumerResponseMapper { Object mapResponse(Response response); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactory; public interface ConsumerResponseMapperFactory extends ResponseMapperFactory { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CseResponseConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { private static final ConsumerResponseMapper SAME = response -> response; @Override public boolean isMatch(Type consumerType) { return Response.class.equals(consumerType); } @Override public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { return SAME; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import org.apache.servicecomb.swagger.invocation.Response; public class DefaultConsumerResponseMapper implements ConsumerResponseMapper { @Override public Object mapResponse(Response response) { return response.getResult(); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class DefaultConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override public int getOrder() { return Integer.MAX_VALUE; } @Override public boolean isMatch(Type consumerType) { return true; } @Override public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { return new DefaultConsumerResponseMapper(); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import java.util.Optional; import org.apache.servicecomb.swagger.invocation.Response; public class OptionalConsumerResponseMapper implements ConsumerResponseMapper { private final ConsumerResponseMapper realMapper; public OptionalConsumerResponseMapper(ConsumerResponseMapper realMapper) { this.realMapper = realMapper; } @Override public Object mapResponse(Response response) { Object realResult = realMapper.mapResponse(response); return Optional.ofNullable(realResult); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Optional; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class OptionalConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override public boolean isMatch(Type consumerType) { if (!ParameterizedType.class.isAssignableFrom(consumerType.getClass())) { return false; } return ((ParameterizedType) consumerType).getRawType().equals(Optional.class); } @Override public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; ConsumerResponseMapper realMapper = factories.createResponseMapper(realConsumerType); return new OptionalConsumerResponseMapper(realMapper); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CompletableFutureProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override public boolean isMatch(Type producerType) { if (!ParameterizedType.class.isAssignableFrom(producerType.getClass())) { return false; } return ((ParameterizedType) producerType).getRawType().equals(CompletableFuture.class); } @Override public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; return factories.createResponseMapper(realProducerType); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CseResponseProducerResponseMapperFactory implements ProducerResponseMapperFactory { private static final ProducerResponseMapper SAME = (status, response) -> (Response) response; @Override public boolean isMatch(Type producerType) { return producerType.equals(Response.class); } @Override public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { return SAME; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.Response; public class DefaultProducerResponseMapper implements ProducerResponseMapper { @Override public Response mapResponse(StatusType status, Object response) { return Response.create(status, response); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class DefaultProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override public int getOrder() { return Integer.MAX_VALUE; } @Override public boolean isMatch(Type producerType) { return true; } @Override public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { return new DefaultProducerResponseMapper(); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import java.util.Optional; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.Response; public class OptionalProducerResponseMapper implements ProducerResponseMapper { private final ProducerResponseMapper realMapper; public OptionalProducerResponseMapper(ProducerResponseMapper realMapper) { this.realMapper = realMapper; } @Override public Response mapResponse(StatusType status, Object response) { @SuppressWarnings("unchecked") Optional optional = (Optional) response; return realMapper.mapResponse(status, optional.orElse(null)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Optional; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class OptionalProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override public boolean isMatch(Type producerType) { if (!ParameterizedType.class.isAssignableFrom(producerType.getClass())) { return false; } return ((ParameterizedType) producerType).getRawType().equals(Optional.class); } @Override public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; ProducerResponseMapper realMapper = factories.createResponseMapper(realProducerType); return new OptionalProducerResponseMapper(realMapper); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.Response; public interface ProducerResponseMapper { Response mapResponse(StatusType status, Object response); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactory; public interface ProducerResponseMapperFactory extends ResponseMapperFactory { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.generator.ScbResponseProcessor ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.generator.InvocationContextProcessor ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerInvocationContextMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerInvocationContextMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.converter.impl.part.InputStreamToPartConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.BytesToPartConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.FileToPartConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.PartToPartConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartArrayConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartListConverter ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.response.consumer.CseResponseConsumerResponseMapperFactory org.apache.servicecomb.swagger.invocation.response.consumer.CompletableFutureConsumerResponseMapperFactory org.apache.servicecomb.swagger.invocation.response.consumer.DefaultConsumerResponseMapperFactory org.apache.servicecomb.swagger.invocation.response.consumer.OptionalConsumerResponseMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.response.producer.CseResponseProducerResponseMapperFactory org.apache.servicecomb.swagger.invocation.response.producer.CompletableFutureProducerResponseMapperFactory org.apache.servicecomb.swagger.invocation.response.producer.DefaultProducerResponseMapperFactory org.apache.servicecomb.swagger.invocation.response.producer.OptionalProducerResponseMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestException.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import java.lang.reflect.InvocationTargetException; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestException { @Test public void testCommonExceptionData() { CommonExceptionData oData = new CommonExceptionData(); oData.setMessage("this is Common exception message"); Assertions.assertEquals("this is Common exception message", oData.getMessage()); oData = new CommonExceptionData("this is a test"); Assertions.assertEquals("this is a test", oData.getMessage()); Assertions.assertEquals("CommonExceptionData [message=this is a test]", oData.toString()); } @Test public void testInvocationException() { InvocationException oExceptionIn = new InvocationException(Status.OK, "I am gone now"); oExceptionIn = ExceptionFactory.convertConsumerException(new Throwable()); Assertions.assertEquals(490, oExceptionIn.getStatusCode()); oExceptionIn = ExceptionFactory.convertConsumerException(new Throwable(), "abc"); Assertions.assertEquals(490, oExceptionIn.getStatusCode()); Assertions.assertEquals("abc", ((CommonExceptionData) oExceptionIn.getErrorData()).getMessage()); oExceptionIn = ExceptionFactory.convertProducerException(new Throwable()); Assertions.assertEquals(590, oExceptionIn.getStatusCode()); oExceptionIn = ExceptionFactory.convertProducerException(new Throwable(), "abcd"); Assertions.assertEquals(590, oExceptionIn.getStatusCode()); Assertions.assertEquals("abcd", ((CommonExceptionData) oExceptionIn.getErrorData()).getMessage()); oExceptionIn = ExceptionFactory.convertConsumerException(new InvocationException(Status.OK, new String("fake-object"))); Assertions.assertEquals(200, oExceptionIn.getStatusCode()); oExceptionIn = ExceptionFactory.convertConsumerException(new InvocationTargetException(new Throwable())); Assertions.assertNotEquals("java.lang.Throwable", oExceptionIn.getMessage()); InvocationException oTemp = new InvocationException(Status.OK, new CommonExceptionData("testObject")); Assertions.assertEquals("OK", oTemp.getReasonPhrase()); Assertions.assertEquals("CommonExceptionData [message=testObject]", (oTemp.getErrorData().toString())); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.core; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestResponse { Response response; AsyncResponse ar = new AsyncResponse() { @Override public void handle(Response resp) { response = resp; } }; @Test public void testAr() { ar.success(Status.ACCEPTED, 1); Assertions.assertTrue(response.isSucceed()); Assertions.assertFalse(response.isFailed()); Assertions.assertEquals(1, (int) response.getResult()); Assertions.assertEquals(Status.ACCEPTED.getStatusCode(), response.getStatusCode()); Assertions.assertEquals(Status.ACCEPTED.getReasonPhrase(), response.getReasonPhrase()); Assertions.assertEquals(Status.ACCEPTED, response.getStatus()); ar.success(2); Assertions.assertEquals(2, (int) response.getResult()); Assertions.assertEquals(Status.OK.getStatusCode(), response.getStatusCode()); Response r = Response.succResp(3); ar.complete(r); Assertions.assertEquals(r, response); ar.consumerFail(new RuntimeExceptionWithoutStackTrace("abc")); CommonExceptionData data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); Assertions.assertEquals("Unexpected consumer error, please check logs for details", data.getMessage()); Assertions.assertEquals(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, response.getStatusCode()); ar.fail(InvocationType.CONSUMER, new RuntimeExceptionWithoutStackTrace("abc")); data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); Assertions.assertEquals("Unexpected consumer error, please check logs for details", data.getMessage()); Assertions.assertEquals(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, response.getStatusCode()); InvocationException consumerException = new InvocationException(300, "abc", "def"); ar.consumerFail(consumerException); Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); Assertions.assertEquals(300, response.getStatusCode()); ar.fail(InvocationType.CONSUMER, consumerException); Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); Assertions.assertEquals(300, response.getStatusCode()); ar.producerFail(new RuntimeExceptionWithoutStackTrace("abc")); data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); Assertions.assertEquals("Unexpected producer error, please check logs for details", data.getMessage()); Assertions.assertEquals(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, response.getStatusCode()); ar.fail(InvocationType.PROVIDER, new RuntimeExceptionWithoutStackTrace("abc")); data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); Assertions.assertEquals("Unexpected producer error, please check logs for details", data.getMessage()); Assertions.assertEquals(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, response.getStatusCode()); InvocationException producerException = new InvocationException(500, "abc", "def"); ar.producerFail(producerException); Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); Assertions.assertEquals(500, response.getStatusCode()); ar.fail(InvocationType.PROVIDER, producerException); Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); Assertions.assertEquals(500, response.getStatusCode()); } @Test public void test() { Response r = Response.create(200, "200", 2); Assertions.assertEquals(200, r.getStatusCode()); Assertions.assertEquals(2, (int) r.getResult()); Response r1 = r.build(); Assertions.assertEquals(r, r1); r = Response.create(300, "300", 3); Assertions.assertEquals(300, r.getStatusCode()); Assertions.assertEquals("300", r.getReasonPhrase()); Assertions.assertEquals(3, ((InvocationException) r.getResult()).getErrorData()); r = Response.createSuccess(Status.OK, 2); Assertions.assertEquals(200, r.getStatusCode()); Assertions.assertEquals(2, (int) r.getResult()); r = Response.success(2, Status.OK); Assertions.assertEquals(200, r.getStatusCode()); Assertions.assertEquals(2, (int) r.getResult()); r = Response.createFail(InvocationType.CONSUMER, "abc"); Assertions.assertEquals("CommonExceptionData [message=abc]", ((InvocationException) r.getResult()).getErrorData().toString()); Assertions.assertEquals(490, r.getStatusCode()); r = Response.createFail(InvocationType.PROVIDER, "def"); Assertions.assertEquals("CommonExceptionData [message=def]", ((InvocationException) r.getResult()).getErrorData().toString()); Assertions.assertEquals(590, r.getStatusCode()); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.models.ProducerImpl; import org.hamcrest.MatcherAssert; import org.junit.BeforeClass; import org.junit.Test; import io.swagger.v3.oas.models.OpenAPI; import org.junit.jupiter.api.Assertions; public class TestSwaggerEnvironment { private static final SwaggerEnvironment env = new SwaggerEnvironment(); private static SwaggerProducer producer; @BeforeClass public static void init() { producer = env.createProducer(new ProducerImpl(), null); } @Test public void ableToFindVisibleMethod() { MatcherAssert.assertThat(producer.findOperation("visibleMethod"), is(notNullValue())); } @Test public void unableToFindHiddenMethod() { MatcherAssert.assertThat(producer.findOperation("hiddenMethod"), is(nullValue())); } interface ConsumerIntf { void exist(); void notExist(); } interface ContractIntf { void exist(); } @Test public void createConsumer_consumerMethodSetBigger() { OpenAPI swagger = SwaggerGenerator.generate(ContractIntf.class); SwaggerConsumer swaggerConsumer = env.createConsumer(ConsumerIntf.class, swagger); Assertions.assertNotNull(swaggerConsumer.findOperation("exist")); Assertions.assertNull(swaggerConsumer.findOperation("notExist")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerProducerOperation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.engine; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.models.PojoImpl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestSwaggerProducerOperation { private static final SwaggerEnvironment env = new SwaggerEnvironment(); private static SwaggerProducer producer; @Test public void testGetParameterType() { PojoImpl pojo = new PojoImpl(); producer = env.createProducer(pojo, null); SwaggerProducerOperation swaggerProducerOperation = producer.findOperation("testBytes"); Assertions.assertEquals(true, swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody() != null); Assertions.assertEquals(null, swaggerProducerOperation.getSwaggerParameterType("bytes")); swaggerProducerOperation = producer.findOperation("testSimple"); Assertions.assertEquals(true, swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody() != null); Assertions.assertEquals(Object.class, swaggerProducerOperation.getSwaggerParameterType( (String) swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody().getExtensions() .get(SwaggerConst.EXT_BODY_NAME))); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/TestSwaggerInvocation.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestSwaggerInvocation { @Test public void construct_withContext() { InvocationContext parentContext = new InvocationContext(); parentContext.addContext("k", "v"); parentContext.addLocalContext("k", 1); ContextUtils.setInvocationContext(parentContext); try { SwaggerInvocation invocation = new SwaggerInvocation(); Assertions.assertSame(parentContext, invocation.getParentContext()); Assertions.assertEquals("v", invocation.getContext("k")); Assertions.assertEquals(1, (int) invocation.getLocalContext("k")); } finally { ContextUtils.removeInvocationContext(); } } @Test public void construct_noContext() { SwaggerInvocation invocation = new SwaggerInvocation(); Assertions.assertNull(invocation.getParentContext()); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestJaxrsV1V1 { @Test public void should_mapper_consumer_multi_args_to_swagger_multi_args() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } interface ConsumerAddV1_diff_order { int add(int y, int x); } @Test public void should_mapper_consumer_multi_args_to_swagger_multi_args_with_diff_order() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1_diff_order.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void should_mapper_consumer_multi_args_to_swagger_multi_args_gen_by_BeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void should_mapper_consumer_multi_args_to_swagger_body() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void should_mapper_consumer_wrapped_body_to_swagger_multi_args() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void should_mapper_consumer_wrapped_body_to_swagger_multi_args_gen_by_BeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void should_mapper_consumer_body_to_swagger_body() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV2; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestJaxrsV1V2 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void add_addBeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void addBody_addBeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestJaxrsV2V1 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_addBeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV2; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestJaxrsV2V2 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void add_addBeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_addBeanParam() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoOneArg.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.User; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerOneArg; import org.apache.servicecomb.swagger.invocation.schemas.PojoOneArg; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestPojoOneArg { @Test public void should_mapper_consumer_simple_to_swagger_body() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoOneArg.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerOneArg.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("simple").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("name", "name"); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("simpleBody"); Assertions.assertEquals(1, result.size()); Assertions.assertEquals("name", result.get("name")); } @Test public void should_mapper_consumer_bean_to_swagger_body() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoOneArg.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerOneArg.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("bean").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("user", new User()); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); Assertions.assertSame(arguments.get("user"), result.get("user")); } @Test public void should_mapper_consumer_enum_to_swagger_body_field() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoOneArg.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerOneArg.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("enumBody").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("color", Color.BLUE); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("enumBodyBody"); Assertions.assertEquals(1, result.size()); Assertions.assertEquals(Color.BLUE, result.get("color")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddWithContext; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestPojoV1V1 { @Test public void add_add_class() { add_add(PojoAddV1.class); } @Test public void add_add_interface() { add_add(ConsumerAddV1.class); } public void add_add(Class clazz) { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(clazz); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); AddWrapperV1 wrapperV1 = (AddWrapperV1) result.get("addBody"); Assertions.assertEquals(1, wrapperV1.getX()); Assertions.assertEquals(2, wrapperV1.y); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } @Test public void addWithContext_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddWithContext.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); InvocationContext invocationContext = new InvocationContext(); invocationContext.addContext("k1", "v1"); invocationContext.addContext("k2", "v2"); invocationContext.addLocalContext("k3", "v3"); invocationContext.addLocalContext("k4", "v4"); Map arguments = new HashMap<>(); arguments.put("context", invocationContext); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, result.get("x")); Assertions.assertEquals(2, result.get("y")); Assertions.assertEquals(2, invocation.getContext().size()); Assertions.assertEquals("v1", invocation.getContext().get("k1")); Assertions.assertEquals("v2", invocation.getContext().get("k2")); Assertions.assertEquals(2, invocation.getLocalContext().size()); Assertions.assertEquals("v3", invocation.getLocalContext().get("k3")); Assertions.assertEquals("v4", invocation.getLocalContext().get("k4")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestPojoV1V2 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestPojoV2V1 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestPojoV2V2 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.Test; import io.swagger.v3.oas.models.OpenAPI; import org.junit.jupiter.api.Assertions; @SuppressWarnings("unchecked") public class TestSpringmvcV1V1 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.Test; import io.swagger.v3.oas.models.OpenAPI; import org.junit.jupiter.api.Assertions; @SuppressWarnings("unchecked") public class TestSpringmvcV1V2 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void add_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void addBody_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertNull(result.get("x-z")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV1(1, 2)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.junit.Test; import io.swagger.v3.oas.models.OpenAPI; import org.junit.jupiter.api.Assertions; @SuppressWarnings("unchecked") public class TestSpringmvcV2V1 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("x-z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.consumer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.models.OpenAPI; @SuppressWarnings("unchecked") public class TestSpringmvcV2V2 { @Test public void add_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void add_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void add_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); result = (Map) result.get("addBody"); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_add() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_addWrapper() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); Assertions.assertEquals(3, (int) result.get("x-z")); } @Test public void addBody_addBody() { SwaggerEnvironment environment = new SwaggerEnvironment(); OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("addBody", new AddWrapperV2(1, 2, 3)); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestJaxrs.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddBeanParamV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestJaxrs { @Test public void add() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new JaxrsAddV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBeanParam() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new JaxrsAddBeanParamV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); AddBeanParamV1 paramV1 = (AddBeanParamV1) result.get("wrapper"); Assertions.assertEquals(1, paramV1.getX()); Assertions.assertEquals(2, paramV1.y); } @Test public void addBody() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new JaxrsAddBodyV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); AddWrapperV1 addBody = new AddWrapperV1(); Map arguments = new HashMap<>(); arguments.put("addBody", addBody); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); Assertions.assertSame(addBody, result.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojo.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV1; import org.apache.servicecomb.swagger.invocation.schemas.PojoAddWithContextV1; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestPojo { static Map addBody = new HashMap<>(); static { addBody.put("x", 1); addBody.put("y", 2); } @Test public void add() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoAddV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); SwaggerInvocation invocation = new SwaggerInvocation(); Map arguments = new HashMap<>(); arguments.put("addBody", addBody); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addBody() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoAddBodyV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); SwaggerInvocation invocation = new SwaggerInvocation(); Map arguments = new HashMap<>(); arguments.put("addBody", addBody); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); Assertions.assertSame(addBody, result.get("addBody")); } @Test public void addWithContext_add() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoAddWithContextV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); SwaggerInvocation invocation = new SwaggerInvocation(); Map arguments = new HashMap<>(); arguments.put("addBody", addBody); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(3, result.size()); Assertions.assertSame(invocation, result.get("context")); Assertions.assertEquals(1, result.get("x")); Assertions.assertEquals(2, result.get("y")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojoOneArg.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.schemas.PojoOneArg; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestPojoOneArg { @Test public void should_mapper_swagger_wrapped_body_field_to_producer_enum() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoOneArg(), null); SwaggerProducerOperation swaggerProducerOperation = swaggerProducer.findOperation("enumBody"); Assertions.assertEquals("enumBodyBody", swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody().getExtensions().get(SwaggerConst.EXT_BODY_NAME)); ProducerArgumentsMapper mapper = swaggerProducerOperation.getArgumentsMapper(); SwaggerInvocation invocation = new SwaggerInvocation(); Map swaggerArguments = new HashMap<>(); Map arguments = new HashMap<>(); arguments.put("color", "BLUE"); swaggerArguments.put("enumBodyBody", arguments); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, swaggerArguments); Assertions.assertEquals(1, result.size()); Assertions.assertSame(Color.BLUE, result.get("color")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestSpringmvc.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.arguments.producer; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV1; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV1; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestSpringmvc { @Test public void add() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new SpringmvcAddV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(2, result.size()); Assertions.assertEquals(1, (int) result.get("x")); Assertions.assertEquals(2, (int) result.get("y")); } @Test public void addWrapper() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new SpringmvcAddWrapperV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); AddWrapperV1 paramV1 = (AddWrapperV1) result.get("wrapper"); Assertions.assertEquals(1, paramV1.getX()); Assertions.assertEquals(2, paramV1.y); } @Test public void addBody() { SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new SpringmvcAddBodyV1(), null); ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); AddWrapperV1 addBody = new AddWrapperV1(); Map arguments = new HashMap<>(); arguments.put("addBody", addBody); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); Assertions.assertEquals(1, result.size()); Assertions.assertSame(addBody, result.get("addBody")); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestContextUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; import java.util.concurrent.CompletableFuture; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestContextUtils { @Test public void getFromCompletableFuture() { Assertions.assertNull(ContextUtils.getFromCompletableFuture(new CompletableFuture<>())); InvocationContext context = new InvocationContext(); Assertions .assertSame(context, ContextUtils.getFromCompletableFuture(new InvocationContextCompletableFuture<>(context))); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestInvocationContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.context; import java.util.HashMap; import java.util.Map; import jakarta.ws.rs.core.Response.Status.Family; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestInvocationContext { @Test public void addContext() { InvocationContext invocationContext = new InvocationContext(); invocationContext.addContext("key1", "value1"); Assertions.assertEquals(1, invocationContext.getContext().size()); Assertions.assertEquals("value1", invocationContext.getContext("key1")); Map otherContext = new HashMap<>(); otherContext.put("key2", "value2"); invocationContext.addContext(otherContext); Assertions.assertEquals(2, invocationContext.getContext().size()); Assertions.assertEquals("value2", invocationContext.getContext("key2")); InvocationContext invocationContext2 = new InvocationContext(); Map otherContext2 = new HashMap<>(); otherContext2.put("key3", "value3"); invocationContext2.setContext(otherContext2); invocationContext.addContext(invocationContext2); Assertions.assertEquals(3, invocationContext.getContext().size()); } @Test public void mergeMapContext() { InvocationContext invocationContext = new InvocationContext(); Map otherContext = new HashMap<>(); otherContext.put("key1", "value1"); //otherContext's size is large than old. invocationContext.mergeContext(otherContext); Assertions.assertEquals(1, invocationContext.getContext().size()); Assertions.assertEquals("value1", invocationContext.getContext("key1")); otherContext.put("key1", "value2"); //otherContext's size is not large than old. invocationContext.mergeContext(otherContext); Assertions.assertEquals(1, invocationContext.getContext().size()); Assertions.assertEquals("value2", invocationContext.getContext("key1")); } @Test public void mergeInvocationContext() { InvocationContext invocationContext = new InvocationContext(); Map otherContext = new HashMap<>(); otherContext.put("key1", "value1"); InvocationContext context2 = new InvocationContext(); context2.setContext(otherContext); invocationContext.mergeContext(context2); Assertions.assertEquals(1, invocationContext.getContext().size()); Assertions.assertEquals("value1", invocationContext.getContext("key1")); } @Test public void addLocalContext() { InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext("key1", "value1"); Assertions.assertEquals(1, invocationContext.getLocalContext().size()); Assertions.assertEquals("value1", invocationContext.getLocalContext("key1")); Map otherContext = new HashMap<>(); otherContext.put("key2", "value2"); invocationContext.addLocalContext(otherContext); Assertions.assertEquals(2, invocationContext.getLocalContext().size()); Assertions.assertEquals("value2", invocationContext.getLocalContext("key2")); } @Test public void setStatus() { InvocationContext invocationContext = new InvocationContext(); invocationContext.setStatus(200); System.out.println(invocationContext.getStatus().getFamily()); Assertions.assertEquals(200, invocationContext.getStatus().getStatusCode()); Assertions.assertEquals("OK", invocationContext.getStatus().getReasonPhrase()); Assertions.assertEquals(Family.SUCCESSFUL, invocationContext.getStatus().getFamily()); invocationContext.setStatus(200, "TEST"); Assertions.assertEquals(200, invocationContext.getStatus().getStatusCode()); Assertions.assertEquals("TEST", invocationContext.getStatus().getReasonPhrase()); Assertions.assertEquals(Family.SUCCESSFUL, invocationContext.getStatus().getFamily()); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.util.Arrays; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.FilePart; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class PartListToPartArrayConverterTest { PartListToPartArrayConverter converter = new PartListToPartArrayConverter(); @Test public void getSrcType() { Assertions.assertEquals("java.util.List", converter.getSrcType().getTypeName()); } @Test public void getTargetType() { Assertions.assertEquals(Part[].class.getCanonicalName(), converter.getTargetType().getTypeName()); } @Test public void convert() { Object parts = converter.convert(Arrays.asList(new FilePart("name", "file"))); MatcherAssert.assertThat(parts, Matchers.instanceOf(Part[].class)); } @Test public void should_got_null_when_convert_null() { Assertions.assertNull(converter.convert(null)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.util.Arrays; import java.util.List; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.FilePart; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class PartListToPartListConverterTest { PartListToPartListConverter converter = new PartListToPartListConverter(); @Test public void getSrcType() { Assertions.assertEquals("java.util.List", converter.getSrcType().getTypeName()); } @Test public void getTargetType() { Assertions.assertEquals("java.util.List", converter.getTargetType().getTypeName()); } @Test public void convert() { List parts = Arrays.asList(new FilePart("name", "file")); Assertions.assertSame(parts, converter.convert(parts)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.part.FilePart; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class PartToPartConverterTest { PartToPartConverter converter = new PartToPartConverter(); @Test public void getSrcType() { Assertions.assertEquals(Part.class.getName(), converter.getSrcType().getTypeName()); } @Test public void getTargetType() { Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); } @Test public void convert() { Part part = new FilePart("name", "file"); Assertions.assertSame(part, converter.convert(part)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestBytesToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import jakarta.servlet.http.Part; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestBytesToPartConverter { BytesToPartConverter converter = new BytesToPartConverter(); @Test public void getSrcType() { Assertions.assertSame(byte[].class, converter.getSrcType()); } @Test public void getTargetType() { Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); } @Test public void convert() { Object part = converter.convert(new byte[] {}); MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestFileToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.io.File; import jakarta.servlet.http.Part; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFileToPartConverter { FileToPartConverter converter = new FileToPartConverter(); @Test public void getSrcType() { Assertions.assertEquals(File.class.getName(), converter.getSrcType().getTypeName()); } @Test public void getTargetType() { Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); } @Test public void convert() { File file = new File("abc"); Object part = converter.convert(file); MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestInputStreamToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.io.ByteArrayInputStream; import java.io.InputStream; import jakarta.servlet.http.Part; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestInputStreamToPartConverter { InputStreamToPartConverter converter = new InputStreamToPartConverter(); @Test public void getSrcType() { Assertions.assertEquals(InputStream.class.getName(), converter.getSrcType().getTypeName()); } @Test public void getTargetType() { Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); } @Test public void convert() { Object part = converter.convert(new ByteArrayInputStream(new byte[] {})); MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestResourceToPartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter.impl.part; import java.io.ByteArrayInputStream; import jakarta.servlet.http.Part; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; public class TestResourceToPartConverter { ResourceToPartConverter converter = new ResourceToPartConverter(); @Test public void getSrcType() { Assertions.assertEquals(Resource.class.getName(), converter.getSrcType().getTypeName()); } @Test public void getTargetType() { Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); } @Test public void convert() { Object part = converter.convert(new InputStreamResource(new ByteArrayInputStream(new byte[] {}))); MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.exception; import static com.google.common.collect.ImmutableMap.of; import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; class CommonExceptionDataTest { @Test void should_not_include_code_in_json_when_code_is_null() { CommonExceptionData data = new CommonExceptionData("msg"); assertThat(Json.encode(data)).isEqualTo("{\"message\":\"msg\"}"); } @Test void should_include_code_in_json_when_code_is_not_null() { CommonExceptionData data = new CommonExceptionData("code", "msg"); String json = Json.encode(data); @SuppressWarnings("unchecked") Map obj = Json.decodeValue(json, Map.class); assertThat(obj).containsEntry("code", "code").containsEntry("message", "msg").hasSize(2); } @Test void should_include_dynamic_field_in_json() { CommonExceptionData data = new CommonExceptionData("msg"); data.putDynamic("k", "v"); assertThat(Json.encode(data)).isEqualTo("{\"message\":\"msg\",\"k\":\"v\"}"); } @Test void should_decode_dynamic_field_from_json() { String json = "{\"message\":\"msg\",\"k\":\"v\"}"; CommonExceptionData data = Json.decodeValue(json, CommonExceptionData.class); assertThat(data.getMessage()).isEqualTo("msg"); assertThat(data.getDynamic()).isEqualTo(of("k", "v")); assertThat(Json.encode(data)).isEqualTo(json); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/JaxrsImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.models; import java.util.Arrays; import java.util.List; import jakarta.ws.rs.CookieParam; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; @Path("/JaxrsImpl") @Produces(MediaType.APPLICATION_JSON) public class JaxrsImpl { @Path("/testTwoSimple") @GET public int testSimple(@PathParam("a") int a, @QueryParam("b") int b, @HeaderParam("c") int c) { return a - b - c; } @Path("/testObject") @POST public Person testObject(Person user) { user.setName("hello " + user.getName()); return user; } @Path("/testSimpleAndObject") @POST public String testSimpleAndObject(@CookieParam("prefix") String prefix, Person user) { return prefix + " " + user.getName(); } @Path("/testContext") @POST public String testContext(InvocationContext context, @FormParam("form") String name) { context.addContext("name", name); return name + " sayhi"; } @Path("/bytes") @POST public byte[] testBytes(byte[] input) { return input; } @Path("/listBytes") @POST public List testListBytes(List bytes) { return bytes; } @Path("/testArrayArray") @POST public String[] testArrayArray(String[] s) { return s; } @Path("/testArrayList") @POST public List testArrayList(String[] s) { return Arrays.asList(s); } @Path("/testListArray") @POST public String[] testListArray(List s) { return s.toArray(new String[0]); } @Path("/testListList") @POST public List testListList(List s) { return s; } @Path("/testObjectArrayArray") @POST public Person[] testObjectArrayArray(Person[] s) { return s; } @Path("/testObjectArrayList") @POST public List testObjectArrayList(Person[] s) { return Arrays.asList(s); } @Path("/testObjectListArray") @POST public Person[] testObjectListArray(List s) { return s.toArray(new Person[0]); } @Path("/testObjectListList") @POST public List testObjectListList(List s) { return s; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/Person.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.models; public class Person { private String name; public Person() { } public Person(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } @Override public String toString() { return name; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoConsumerIntf.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.models; import java.util.List; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import io.swagger.v3.oas.annotations.Operation; public interface PojoConsumerIntf { int testSimple(int a, int b, int c); Person testObject(Person user); @Operation(operationId = "testObject", summary = "") CompletableFuture testObjectAsync(Person user); String testSimpleAndObject(String prefix, Person user); String testContext(InvocationContext context, String name); List testListBytes(List bytes); byte[] testBytes(byte[] bytes); String[] testArrayArray(String[] s); List testArrayList(String[] s); String[] testListArray(List s); List testListList(List s); Person[] testObjectArrayArray(Person[] s); List testObjectArrayList(Person[] s); Person[] testObjectListArray(List s); List testObjectListList(List s); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.models; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import io.swagger.v3.oas.annotations.Operation; public class PojoImpl { public int testSimple(int a, int b, int c) { return a - b - c; } public Person testObject(Person user) { user.setName("hello " + user.getName()); return user; } @Operation(operationId = "testSimpleAndObject", summary = "") public CompletableFuture testSimpleAndObjectAsync(String prefix, Person user) { CompletableFuture future = new CompletableFuture<>(); future.complete(prefix + " " + user.getName()); return future; } public String testContext(InvocationContext context, String name) { context.addContext("name", name); return name + " sayhi"; } public List testListBytes(List bytes) { return bytes; } public byte[] testBytes(byte[] bytes) { return bytes; } public String[] testArrayArray(String[] s) { return s; } public List testArrayList(String[] s) { return Arrays.asList(s); } public String[] testListArray(List s) { return s.toArray(new String[0]); } public List testListList(List s) { return s; } public Person[] testObjectArrayArray(Person[] s) { return s; } public List testObjectArrayList(Person[] s) { return Arrays.asList(s); } public Person[] testObjectListArray(List s) { return s.toArray(new Person[0]); } public List testObjectListList(List s) { return s; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/ProducerImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.models; import io.swagger.v3.oas.annotations.Operation; public class ProducerImpl { @Operation(summary = "", hidden = true) public int hiddenMethod(int a) { return a; } @Operation(summary = "") public int visibleMethod(int a) { return a; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/SpringmvcImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.models; public class SpringmvcImpl { } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/TestResponsesMeta.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.junit.Test; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; public class TestResponsesMeta { static class ResponseMetaImpl { @ApiResponses({@ApiResponse(responseCode = "400", description = "", content = {@Content(schema = @Schema(implementation = String.class))}), @ApiResponse(responseCode = "401", description = "", content = {@Content(schema = @Schema(implementation = String.class))}, headers = {@Header(name = "h1", schema = @Schema(implementation = String.class))}) }) public int add(int x, int y) { return x + y; } } @Test public void test() { OpenAPI swagger = SwaggerGenerator.generate(ResponseMetaImpl.class); Operation operation = swagger.getPaths().get("/add").getPost(); ResponsesMeta meta = new ResponsesMeta(); meta.init(swagger, operation); JavaType resp = meta.findResponseType(200); Assertions.assertEquals(Integer.class, resp.getRawClass()); resp = meta.findResponseType(201); Assertions.assertEquals(Integer.class, resp.getRawClass()); resp = meta.findResponseType(400); Assertions.assertEquals(String.class, resp.getRawClass()); resp = meta.findResponseType(401); Assertions.assertEquals(String.class, resp.getRawClass()); resp = meta.findResponseType(500); // changed to Object for new version to keep user defined error data not lose and can be parsed. Assertions.assertEquals(Object.class, resp.getRawClass()); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.consumer; import static org.assertj.core.api.Assertions.assertThat; import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.models.OpenAPI; public class TestConsumerResponseMapperFactories { interface ConsumerResponseForTest { String normal(); CompletableFuture async(); @ApiResponse(responseCode = "200", description = "", content = {@Content(schema = @Schema(implementation = String.class))}) Response scbResponse(); @ApiResponse(responseCode = "200", description = "", content = {@Content(schema = @Schema(implementation = String.class))}) jakarta.ws.rs.core.Response jaxrsResponse(); Optional optional(); } SwaggerEnvironment environment = new SwaggerEnvironment(); SwaggerConsumer swaggerConsumer; String result = "abc"; Response response = Response.ok(result); @Before public void setup() { OpenAPI swagger = SwaggerGenerator.generate(ConsumerResponseForTest.class); swaggerConsumer = environment.createConsumer(ConsumerResponseForTest.class, swagger); } @Test public void should_mapper_to_normal_string() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("normal"); assertThat(operation.getResponseMapper()).isInstanceOf(DefaultConsumerResponseMapper.class); Assertions.assertEquals(result, operation.getResponseMapper().mapResponse(response)); } @Test public void should_mapper_to_completableFuture_element_string() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("async"); assertThat(operation.getResponseMapper()).isInstanceOf(DefaultConsumerResponseMapper.class); Assertions.assertEquals(result, operation.getResponseMapper().mapResponse(response)); } @Test public void should_mapper_to_scbResponse_string() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("scbResponse"); assertThat(operation.getResponseMapper().getClass().getName()) .startsWith(CseResponseConsumerResponseMapperFactory.class.getName()); Response scbResponse = (Response) operation.getResponseMapper().mapResponse(response); Assertions.assertEquals(result, scbResponse.getResult()); } @Test public void should_mapper_to_optional_string() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("optional"); assertThat(operation.getResponseMapper()).isInstanceOf(OptionalConsumerResponseMapper.class); @SuppressWarnings("unchecked") Optional optional = (Optional) operation.getResponseMapper().mapResponse(response); Assertions.assertEquals(result, optional.get()); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.response.producer; import static org.assertj.core.api.Assertions.assertThat; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.engine.SwaggerProducer; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.BeforeClass; import org.junit.Test; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.core.Response.Status; public class TestProducerResponseMapperFactories { static class ResponseForTest { public String normal() { return "normal"; } public CompletableFuture async() { return CompletableFuture.completedFuture("async"); } @ApiResponse(responseCode = "200", description = "", content = {@Content(schema = @Schema(implementation = String.class))}) public Response scbResponse() { return Response.ok("scb"); } @ApiResponse(responseCode = "200", description = "", content = {@Content(schema = @Schema(implementation = String.class))}) public jakarta.ws.rs.core.Response jaxrsResponse() { return jakarta.ws.rs.core.Response.ok("jaxrs").build(); } public Optional optional() { return Optional.of("optional"); } } static SwaggerEnvironment environment = new SwaggerEnvironment(); static SwaggerProducer swaggerProducer; static ResponseForTest instance = new ResponseForTest(); static String result = "abc"; static Response response = Response.ok(result); @BeforeClass public static void setup() { swaggerProducer = environment.createProducer(instance); } @Test public void should_mapper_to_normal_string() { SwaggerProducerOperation operation = swaggerProducer.findOperation("normal"); assertThat(operation.getResponseMapper()).isInstanceOf(DefaultProducerResponseMapper.class); assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.normal()).getResult()) .isEqualTo("normal"); } @Test public void should_mapper_to_completableFuture_element_string() throws ExecutionException, InterruptedException { SwaggerProducerOperation operation = swaggerProducer.findOperation("async"); assertThat(operation.getResponseMapper()).isInstanceOf(DefaultProducerResponseMapper.class); assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.async().get()).getResult()) .isEqualTo("async"); } @Test public void should_mapper_to_scbResponse_string() { SwaggerProducerOperation operation = swaggerProducer.findOperation("scbResponse"); assertThat(operation.getResponseMapper().getClass().getName()) .startsWith(CseResponseProducerResponseMapperFactory.class.getName()); assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.scbResponse()).getResult()) .isEqualTo("scb"); } @Test public void should_mapper_to_optional_string() { SwaggerProducerOperation operation = swaggerProducer.findOperation("optional"); assertThat(operation.getResponseMapper()).isInstanceOf(OptionalProducerResponseMapper.class); assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.optional()).getResult()) .isEqualTo("optional"); } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; public interface ConsumerAddBodyV1 { int add(AddWrapperV1 addBody); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; public interface ConsumerAddBodyV2 { int add(AddWrapperV2 addBody); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; public interface ConsumerAddV1 { int add(int x, int y); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import io.swagger.v3.oas.annotations.Parameter; public interface ConsumerAddV2 { int add(int x, int y, @Parameter(name = "x-z") int z); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddWithContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public interface ConsumerAddWithContext { int add(InvocationContext context, int x, int y); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerOneArg.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.User; public interface ConsumerOneArg { void simple(String name); void bean(User user); void enumBody(Color color); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import org.apache.servicecomb.swagger.invocation.schemas.models.AddBeanParamV1; @Path("/") public class JaxrsAddBeanParamV1 { @Path("/add") @GET public int add(@BeanParam AddBeanParamV1 wrapper) { return wrapper.getX() + wrapper.y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import org.apache.servicecomb.swagger.invocation.schemas.models.AddBeanParamV2; @Path("/") public interface JaxrsAddBeanParamV2 { @Path("/add") @GET int add(@BeanParam AddBeanParamV2 wrapper); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; @Path("/") public class JaxrsAddBodyV1 { @Path("/add") @POST public int add(AddWrapperV1 addBody) { return addBody.getX() + addBody.y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; @Path("/") public interface JaxrsAddBodyV2 { @Path("/add") @POST int add(AddWrapperV2 addBody); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; @Path("/") public class JaxrsAddV1 { @Path("/add") @GET public int add(@QueryParam("x") int x, @QueryParam("y") int y) { return x + y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; @Path("/") public interface JaxrsAddV2 { @Path("/add") @GET int add(@QueryParam("x") int x, @QueryParam("y") int y, @HeaderParam("x-z") int z); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; public class PojoAddBodyV1 { public int add(AddWrapperV1 addBody) { return addBody.getX() + addBody.y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; public interface PojoAddBodyV2 { int add(AddWrapperV2 addBody); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; public class PojoAddV1 { public int add(int x, int y) { return x + y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import io.swagger.v3.oas.annotations.Parameter; public interface PojoAddV2 { int add(int x, int y, @Parameter(name = "x-z") int z); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddWithContextV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; public class PojoAddWithContextV1 { public int add(InvocationContext context, int x, int y) { return x + y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoOneArg.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.foundation.test.scaffolding.model.Color; import org.apache.servicecomb.foundation.test.scaffolding.model.User; public class PojoOneArg { public void simple(String name) { } public void bean(User user) { } public void enumBody(Color color) { } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping public class SpringmvcAddBodyV1 { @PostMapping(path = "/add") public int add(@RequestBody AddWrapperV1 addBody) { return addBody.getX() + addBody.y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping public interface SpringmvcAddBodyV2 { @PostMapping(path = "/add") int add(@RequestBody AddWrapperV2 addBody); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping public class SpringmvcAddV1 { @GetMapping(path = "/add") public int add(int x, int y) { return x + y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping public interface SpringmvcAddV2 { @GetMapping(path = "/add") int add(int x, int y, @RequestHeader(name = "x-z") int z); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping public class SpringmvcAddWrapperV1 { @GetMapping(path = "/add") public int add(AddWrapperV1 wrapper) { return wrapper.getX() + wrapper.y; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping public interface SpringmvcAddWrapperV2 { @GetMapping(path = "/add") int add(AddWrapperV2 wrapper); } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas.models; import jakarta.ws.rs.QueryParam; public class AddBeanParamV1 { @QueryParam("x") private int x; @QueryParam("y") public int y; public AddBeanParamV1() { } public AddBeanParamV1(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas.models; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.QueryParam; public class AddBeanParamV2 { @QueryParam("x") private int x; @QueryParam("y") public int y; @HeaderParam("x-z") public int z; public AddBeanParamV2(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } public int getX() { return x; } public void setX(int x) { this.x = x; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV1.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas.models; public class AddWrapperV1 { private int x; public int y; public AddWrapperV1() { } public AddWrapperV1(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } } ================================================ FILE: swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV2.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.schemas.models; import com.fasterxml.jackson.annotation.JsonProperty; public class AddWrapperV2 { private int x; public int y; @JsonProperty(value = "x-z") public int z; public AddWrapperV2(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } public int getX() { return x; } public void setX(int x) { this.x = x; } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-invocation 3.4.0-SNAPSHOT swagger-invocation-jaxrs Java Chassis::Swagger::Invocation::JAXRS org.apache.servicecomb swagger-invocation-core org.apache.servicecomb swagger-generator-jaxrs org.apache.logging.log4j log4j-slf4j-impl test org.glassfish.jersey.core jersey-client test ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import java.util.Map.Entry; import jakarta.ws.rs.core.Response.ResponseBuilder; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import io.vertx.core.MultiMap; public class JaxrsConsumerResponseMapper implements ConsumerResponseMapper { @Override public Object mapResponse(Response response) { ResponseBuilder responseBuilder = jakarta.ws.rs.core.Response.status(response.getStatus()).entity(response.getResult()); MultiMap headers = response.getHeaders(); if (headers == null) { return responseBuilder.build(); } for (Entry entry : headers.entries()) { responseBuilder.header(entry.getKey(), entry.getValue()); } return responseBuilder.build(); } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import java.lang.reflect.Type; import jakarta.ws.rs.core.Response; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; public class JaxrsConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override public boolean isMatch(Type consumerType) { return Response.class.equals(consumerType); } @Override public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { return new JaxrsConsumerResponseMapper(); } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import java.util.List; import java.util.Map.Entry; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; public class JaxrsProducerResponseMapper implements ProducerResponseMapper { @Override public Response mapResponse(StatusType status, Object response) { jakarta.ws.rs.core.Response jaxrsResponse = (jakarta.ws.rs.core.Response) response; Response cseResponse = Response.status(jaxrsResponse.getStatusInfo()).entity(jaxrsResponse.getEntity()); MultivaluedMap headers = jaxrsResponse.getHeaders(); for (Entry> entry : headers.entrySet()) { if (entry.getValue() == null) { continue; } for (Object value : entry.getValue()) { if (value == null) { continue; } cseResponse.addHeader(entry.getKey(), value.toString()); } } return cseResponse; } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import java.lang.reflect.Type; import jakarta.ws.rs.core.Response; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; public class JaxrsProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override public boolean isMatch(Type producerType) { return Response.class.equals(producerType); } @Override public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { return new JaxrsProducerResponseMapper(); } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.jaxrs.response.JaxrsConsumerResponseMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.jaxrs.response.JaxrsProducerResponseMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.models.OpenAPI; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; public class TestJaxrsConsumerResponseMapper { @Path("/") interface ConsumerResponseForTest { @ApiResponse(responseCode = "200", description = "", content = @Content(schema = @Schema(implementation = String.class))) @Path("/jaxrsResponse") @GET jakarta.ws.rs.core.Response jaxrsResponse(); } SwaggerEnvironment environment = new SwaggerEnvironment(); SwaggerConsumer swaggerConsumer; String result = "abc"; org.apache.servicecomb.swagger.invocation.Response response = org.apache.servicecomb.swagger.invocation.Response .ok(result); @BeforeEach public void setup() { OpenAPI swagger = SwaggerGenerator.generate(ConsumerResponseForTest.class); swaggerConsumer = environment.createConsumer(ConsumerResponseForTest.class, swagger); } @SuppressWarnings("unchecked") @Test public void jaxrsResponse() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("jaxrsResponse"); Response jaxrsResponse = (Response) operation.getResponseMapper().mapResponse(response); Assertions.assertEquals(result, jaxrsResponse.getEntity()); Assertions.assertTrue(jaxrsResponse.getHeaders().isEmpty()); } @Test public void jaxrsResponseWithHeaders() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("jaxrsResponse"); response.addHeader("h", "v1").addHeader("h", "v2").addHeader("h", null); response.addHeader("h1", null); Response jaxrsResponse = (Response) operation.getResponseMapper().mapResponse(response); Assertions.assertEquals(result, jaxrsResponse.getEntity()); Assertions.assertEquals(1, jaxrsResponse.getHeaders().size()); MatcherAssert.assertThat(jaxrsResponse.getHeaders().get("h"), Matchers.contains("v1", "v2")); } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.swagger.invocation.Response; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestJaxrsProducerResponseMapper { JaxrsProducerResponseMapper mapper = new JaxrsProducerResponseMapper(); @Test public void mapResponse_withoutHeaders() { jakarta.ws.rs.core.Response jaxrsResponse = Mockito.mock(jakarta.ws.rs.core.Response.class); Mockito.when(jaxrsResponse.getStatusInfo()).thenReturn(Status.OK); Mockito.when(jaxrsResponse.getEntity()).thenReturn("result"); Mockito.when(jaxrsResponse.getHeaders()).thenReturn(new MultivaluedHashMap<>()); Response response = mapper.mapResponse(null, jaxrsResponse); Assertions.assertEquals(Status.OK, response.getStatus()); Assertions.assertEquals("result", response.getResult()); Assertions.assertNull(response.getHeaders()); } @Test public void mapResponse_withHeaders() { jakarta.ws.rs.core.Response jaxrsResponse = Mockito.mock(jakarta.ws.rs.core.Response.class); Mockito.when(jaxrsResponse.getStatusInfo()).thenReturn(Status.OK); Mockito.when(jaxrsResponse.getEntity()).thenReturn("result"); MultivaluedMap headers = new MultivaluedHashMap<>(); headers.add("h", "v"); Mockito.when(jaxrsResponse.getHeaders()).thenReturn(headers); Response response = mapper.mapResponse(null, jaxrsResponse); Assertions.assertEquals(Status.OK, response.getStatus()); Assertions.assertEquals("result", response.getResult()); Assertions.assertEquals(1, response.getHeaders().size()); MatcherAssert.assertThat(response.getHeaders("h"), Matchers.contains("v")); } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.jaxrs.response; import jakarta.ws.rs.core.Response; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestJaxrsProducerResponseMapperFactory { JaxrsProducerResponseMapperFactory factory = new JaxrsProducerResponseMapperFactory(); @Test public void isMatch_true() { Assertions.assertTrue(factory.isMatch(Response.class)); } @Test public void isMatch_false() { Assertions.assertFalse(factory.isMatch(String.class)); } @Test public void createResponseMapper() { MatcherAssert.assertThat(factory.createResponseMapper(null, null), Matchers.instanceOf(JaxrsProducerResponseMapper.class)); } } ================================================ FILE: swagger/swagger-invocation/invocation-jaxrs/src/test/resources/log4j2.xml ================================================ ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-invocation 3.4.0-SNAPSHOT swagger-invocation-springmvc Java Chassis::Swagger::Invocation::Spring MVC org.apache.servicecomb swagger-invocation-core org.springframework spring-webmvc org.apache.logging.log4j log4j-slf4j-impl test org.jmockit jmockit test ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter; import java.lang.reflect.Type; import java.util.List; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.springframework.web.multipart.MultipartFile; import jakarta.servlet.http.Part; public class PartListToMultipartArrayConverter implements Converter { @Override public Type getSrcType() { return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Type getTargetType() { return MultipartFile[].class; } @Override public Object convert(Object value) { if (value == null) { return null; } @SuppressWarnings("unchecked") List partList = (List) value; PartToMultipartFile[] partArray = new PartToMultipartFile[partList.size()]; for (int i = 0; i < partArray.length; i++) { partArray[i] = new PartToMultipartFile(partList.get(i)); } return partArray; } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.springframework.web.multipart.MultipartFile; import jakarta.servlet.http.Part; public class PartListToMultipartListConverter implements Converter { @Override public Type getSrcType() { return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Type getTargetType() { return ParameterizedTypeUtil.make(List.class, MultipartFile.class); } @Override public Object convert(Object value) { if (value == null) { return null; } @SuppressWarnings("unchecked") List partList = (List) value; List fileList = new ArrayList<>(); partList.forEach(part -> fileList.add(new PartToMultipartFile(part))); return fileList; } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartConverter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter; import java.lang.reflect.Type; import jakarta.servlet.http.Part; import org.springframework.web.multipart.MultipartFile; public class PartToMultipartConverter implements Converter { @Override public Type getSrcType() { return Part.class; } @Override public Type getTargetType() { return MultipartFile.class; } @Override public Object convert(Object value) { if (value == null) { return null; } return new PartToMultipartFile((Part) value); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartFile.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter; import java.io.File; import java.io.IOException; import java.io.InputStream; import jakarta.servlet.http.Part; import org.apache.commons.io.IOUtils; import org.springframework.web.multipart.MultipartFile; public class PartToMultipartFile implements MultipartFile { private final Part part; public PartToMultipartFile(Part part) { this.part = part; } @Override public String getName() { return part.getName(); } @Override public String getOriginalFilename() { return part.getSubmittedFileName(); } @Override public String getContentType() { return part.getContentType(); } @Override public boolean isEmpty() { return part.getSize() == 0; } @Override public long getSize() { return part.getSize(); } @Override public byte[] getBytes() throws IOException { try (InputStream is = getInputStream()) { return IOUtils.toByteArray(is); } } @Override public InputStream getInputStream() throws IOException { return part.getInputStream(); } @Override public void transferTo(File dest) throws IOException, IllegalStateException { part.write(dest.getPath()); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.util.Map.Entry; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import io.vertx.core.MultiMap; public class SpringmvcConsumerResponseMapper implements ConsumerResponseMapper { private final ConsumerResponseMapper realMapper; public SpringmvcConsumerResponseMapper(ConsumerResponseMapper realMapper) { this.realMapper = realMapper; } @Override public Object mapResponse(Response response) { HttpStatus status = HttpStatus.valueOf(response.getStatusCode()); MultiMap headers = response.getHeaders(); if (headers == null) { Object realResult = realMapper.mapResponse(response); return new ResponseEntity<>(realResult, null, status); } HttpHeaders httpHeaders = new HttpHeaders(); for (Entry entry : headers.entries()) { httpHeaders.add(entry.getKey(), entry.getValue()); } Object realResult = realMapper.mapResponse(response); return new ResponseEntity<>(realResult, httpHeaders, status); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; import org.springframework.http.ResponseEntity; public class SpringmvcConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override public boolean isMatch(Type consumerType) { if (!ParameterizedType.class.isAssignableFrom(consumerType.getClass())) { return false; } return ((ParameterizedType) consumerType).getRawType().equals(ResponseEntity.class); } @Override public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; ConsumerResponseMapper realMapper = factories.createResponseMapper(realConsumerType); return new SpringmvcConsumerResponseMapper(realMapper); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.util.List; import java.util.Map.Entry; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import jakarta.ws.rs.core.Response.StatusType; public class SpringmvcProducerResponseMapper implements ProducerResponseMapper { private final ProducerResponseMapper realMapper; public SpringmvcProducerResponseMapper(ProducerResponseMapper realMapper) { this.realMapper = realMapper; } @SuppressWarnings("unchecked") @Override public Response mapResponse(StatusType status, Object response) { ResponseEntity springmvcResponse = (ResponseEntity) response; // TODO: maybe can use swagger HttpStatus instead of customized one StatusType responseStatus = new HttpStatus(springmvcResponse.getStatusCode().value(), ""); Response cseResponse = null; if (HttpStatus.isSuccess(responseStatus)) { cseResponse = realMapper.mapResponse(responseStatus, springmvcResponse.getBody()); } else { // not support fail response mapper now cseResponse = Response.status(responseStatus).entity(springmvcResponse.getBody()); } HttpHeaders headers = springmvcResponse.getHeaders(); for (Entry> entry : headers.entrySet()) { if (entry.getValue() == null) { continue; } for (String value : entry.getValue()) { cseResponse.addHeader(entry.getKey(), value); } } return cseResponse; } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; import org.springframework.http.ResponseEntity; public class SpringmvcProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override public boolean isMatch(Type producerType) { if (!ParameterizedType.class.isAssignableFrom(producerType.getClass())) { return false; } return ((ParameterizedType) producerType).getRawType().equals(ResponseEntity.class); } @Override public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; ProducerResponseMapper realMapper = factories.createResponseMapper(realProducerType); return new SpringmvcProducerResponseMapper(realMapper); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.converter.PartListToMultipartArrayConverter org.apache.servicecomb.swagger.invocation.converter.PartToMultipartConverter org.apache.servicecomb.swagger.invocation.converter.PartListToMultipartListConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.ResourceToPartConverter ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.springmvc.response.SpringmvcConsumerResponseMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.swagger.invocation.springmvc.response.SpringmvcProducerResponseMapperFactory ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/converter/TestPartToMultipartFile.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.converter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import jakarta.servlet.http.Part; import org.apache.servicecomb.foundation.common.Holder; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestPartToMultipartFile { @Mocked Part part; PartToMultipartFile multipartFile; @Before public void setup() { multipartFile = new PartToMultipartFile(part); } @Test public void getName() { String name = "paramName"; new Expectations() { { part.getName(); result = name; } }; Assertions.assertEquals(name, multipartFile.getName()); } @Test public void getOriginalFilename() { String submittedFileName = "fileName"; new Expectations() { { part.getSubmittedFileName(); result = submittedFileName; } }; Assertions.assertEquals(submittedFileName, multipartFile.getOriginalFilename()); } @Test public void getContentType() { String contentType = "json"; new Expectations() { { part.getContentType(); result = contentType; } }; Assertions.assertEquals(contentType, multipartFile.getContentType()); } @Test public void isEmptyTrue() { new Expectations() { { part.getSize(); result = 0; } }; Assertions.assertTrue(multipartFile.isEmpty()); } @Test public void isEmptyFalse() { new Expectations() { { part.getSize(); result = 1; } }; Assertions.assertFalse(multipartFile.isEmpty()); } @Test public void getSize() { long size = 10; new Expectations() { { part.getSize(); result = size; } }; Assertions.assertEquals(size, multipartFile.getSize()); } static class ByteArrayInputStreamForTest extends ByteArrayInputStream { boolean closed; public ByteArrayInputStreamForTest(byte[] buf) { super(buf); } @Override public void close() throws IOException { closed = true; } } @Test public void getBytes_normal() throws IOException { byte[] bytes = new byte[] {1, 2, 3}; ByteArrayInputStreamForTest is = new ByteArrayInputStreamForTest(bytes); new Expectations() { { part.getInputStream(); result = is; } }; Assertions.assertArrayEquals(bytes, multipartFile.getBytes()); Assertions.assertTrue(is.closed); } @Test public void getBytes_exception() throws IOException { new Expectations() { { part.getInputStream(); result = new IOException("open stream failed"); } }; IOException exception = Assertions.assertThrows(IOException.class, () -> multipartFile.getBytes()); Assertions.assertEquals("open stream failed", exception.getMessage()); } @Test public void transferTo() throws IllegalStateException, IOException { File dest = new File("/dest"); Holder destName = new Holder<>(); new MockUp(part) { @Mock void write(String fileName) throws IOException { destName.value = fileName; } }; multipartFile.transferTo(dest); Assertions.assertEquals(dest.getPath(), destName.value); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcConsumerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.apache.servicecomb.swagger.invocation.Response; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.http.ResponseEntity; import io.swagger.v3.oas.models.OpenAPI; public class TestSpringmvcConsumerResponseMapper { interface ConsumerResponseForTest { ResponseEntity responseEntity(); CompletableFuture> asyncResponseEntity(); } SwaggerEnvironment environment = new SwaggerEnvironment(); SwaggerConsumer swaggerConsumer; String result = "abc"; Response response = Response.ok(result); @Before public void setup() { OpenAPI swagger = SwaggerGenerator.generate(ConsumerResponseForTest.class); swaggerConsumer = environment.createConsumer(ConsumerResponseForTest.class, swagger); } @Test public void responseEntity() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("responseEntity"); @SuppressWarnings("unchecked") ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() .mapResponse(response); Assertions.assertEquals(result, responseEntity.getBody()); Assertions.assertTrue(responseEntity.getHeaders().isEmpty()); } @Test public void responseEntityWithHeader() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("responseEntity"); response.addHeader("h", "v"); @SuppressWarnings("unchecked") ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() .mapResponse(response); Assertions.assertEquals(result, responseEntity.getBody()); Assertions.assertEquals(1, responseEntity.getHeaders().size()); MatcherAssert.assertThat(responseEntity.getHeaders().get("h"), Matchers.contains("v")); } @Test public void asyncResponseEntity() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("asyncResponseEntity"); @SuppressWarnings("unchecked") ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() .mapResponse(response); Assertions.assertEquals(result, responseEntity.getBody()); Assertions.assertTrue(responseEntity.getHeaders().isEmpty()); } @Test public void asyncResponseEntityWithHeader() { SwaggerConsumerOperation operation = swaggerConsumer.findOperation("asyncResponseEntity"); response.addHeader("h", "v1").addHeader("h", "v2"); response.addHeader("h1", null); @SuppressWarnings("unchecked") ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() .mapResponse(response); Assertions.assertEquals(result, responseEntity.getBody()); Assertions.assertEquals(1, responseEntity.getHeaders().size()); MatcherAssert.assertThat(responseEntity.getHeaders().get("h"), Matchers.contains("v1", "v2")); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.util.Arrays; import java.util.List; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.StatusType; import org.apache.servicecomb.foundation.common.http.HttpStatus; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestSpringmvcProducerResponseMapper { @Mocked ProducerResponseMapper realMapper; SpringmvcProducerResponseMapper mapper; String[] arrResult = new String[] {"a", "b"}; @Before public void setup() { mapper = new SpringmvcProducerResponseMapper(realMapper); new MockUp(realMapper) { @Mock Response mapResponse(StatusType status, Object response) { if (HttpStatus.isSuccess(status.getStatusCode())) { return Response.ok(Arrays.asList(arrResult)); } return null; } }; } @SuppressWarnings("unchecked") @Test public void mapResponse_withoutHeader_success() { ResponseEntity responseEntity = new ResponseEntity<>(arrResult, org.springframework.http.HttpStatus.OK); Response response = mapper.mapResponse(null, responseEntity); MatcherAssert.assertThat((List) response.getResult(), Matchers.contains("a", "b")); Assertions.assertEquals(Status.OK, response.getStatus()); } @Test public void mapResponse_withoutHeader_fail() { ResponseEntity responseEntity = new ResponseEntity<>(arrResult, org.springframework.http.HttpStatus.BAD_REQUEST); Response response = mapper.mapResponse(null, responseEntity); Assertions.assertSame(arrResult, response.getResult()); Assertions.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus().getStatusCode()); } @Test public void mapResponse_withHeader() { HttpHeaders headers = new HttpHeaders(); headers.add("h", "v"); ResponseEntity responseEntity = new ResponseEntity<>(arrResult, headers, org.springframework.http.HttpStatus.OK); Response response = mapper.mapResponse(null, responseEntity); List hv = response.getHeaders("h"); MatcherAssert.assertThat(hv, Matchers.contains("v")); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.springmvc.response; import java.lang.reflect.Method; import java.util.List; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; public class TestSpringmvcProducerResponseMapperFactory { SpringmvcProducerResponseMapperFactory factory = new SpringmvcProducerResponseMapperFactory(); ResponseMapperFactories factories = new ResponseMapperFactories<>( ProducerResponseMapperFactory.class); public ResponseEntity responseEntity() { return null; } public List list() { return null; } @Test public void isMatch_true() { Method method = ReflectUtils.findMethod(this.getClass(), "responseEntity"); Assertions.assertTrue(factory.isMatch(method.getGenericReturnType())); } @Test public void isMatch_Parameterized_false() { Method method = ReflectUtils.findMethod(this.getClass(), "list"); Assertions.assertFalse(factory.isMatch(method.getGenericReturnType())); } @Test public void isMatch_false() { Assertions.assertFalse(factory.isMatch(String.class)); } @Test public void createResponseMapper() { Method responseEntityMethod = ReflectUtils.findMethod(this.getClass(), "responseEntity"); ProducerResponseMapper mapper = factory .createResponseMapper(factories, responseEntityMethod.getGenericReturnType()); MatcherAssert.assertThat(mapper, Matchers.instanceOf(SpringmvcProducerResponseMapper.class)); ResponseEntity responseEntity = new ResponseEntity<>(new String[] {"a", "b"}, HttpStatus.OK); Response response = mapper.mapResponse(null, responseEntity); MatcherAssert.assertThat(response.getResult(), Matchers.arrayContaining("a", "b")); } } ================================================ FILE: swagger/swagger-invocation/invocation-springmvc/src/test/resources/log4j2.xml ================================================ ================================================ FILE: swagger/swagger-invocation/invocation-validator/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger-invocation 3.4.0-SNAPSHOT swagger-invocation-validator Java Chassis::Swagger::Invocation::Validator org.apache.servicecomb swagger-invocation-core org.hibernate.validator hibernate-validator org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test ================================================ FILE: swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/DefaultParameterNameProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.validator; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import jakarta.validation.ParameterNameProvider; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class DefaultParameterNameProvider implements ParameterNameProvider { private final Map> methodCache = new ConcurrentHashMapEx<>(); @Override public List getParameterNames(Constructor constructor) { return methodCache.computeIfAbsent(constructor, k -> getParameterNamesEx(constructor)); } @Override public List getParameterNames(Method method) { return methodCache.computeIfAbsent(method, k -> getParameterNamesEx(method)); } private List getParameterNamesEx(Executable methodOrConstructor) { Parameter[] parameters = methodOrConstructor.getParameters(); List parameterNames = new ArrayList<>(parameters.length); for (int idx = 0; idx < parameters.length; idx++) { Parameter parameter = parameters[idx]; String parameterName = parameter.isNamePresent() ? parameter.getName() : "arg" + idx; parameterNames.add(parameterName); } return Collections.unmodifiableList(parameterNames); } } ================================================ FILE: swagger/swagger-invocation/invocation-validator/src/test/java/org/apache/servicecomb/swagger/invocation/validator/TestDefaultParameterNameProvider.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.swagger.invocation.validator; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefaultParameterNameProvider { static class ValidatorForTest { static class Student { private String name; int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } private String grade; private int number; public ValidatorForTest() { } public ValidatorForTest(String grade, int number) { this.grade = grade; this.number = number; } public int add(int a, int b) { return a + b; } public String sayHi(String hi) { return hi + " sayhi"; } public Student sayHello(Student student) { return student; } public String setTest(String grade) { this.grade = grade; return this.grade; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } } Class validatorForTest = ValidatorForTest.class; DefaultParameterNameProvider parameterNameProvider = new DefaultParameterNameProvider(); @Test public void testMethod() throws NoSuchMethodException { Method method = validatorForTest.getMethod("add", int.class, int.class); MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("a", "b")); method = validatorForTest.getMethod("sayHi", String.class); MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("hi")); method = validatorForTest.getMethod("sayHello", ValidatorForTest.Student.class); MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("student")); method = validatorForTest.getMethod("setTest", String.class); MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("grade")); method = validatorForTest.getMethod("getNumber"); Assertions.assertTrue(parameterNameProvider.getParameterNames(method).isEmpty()); method = validatorForTest.getMethod("setNumber", int.class); MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("number")); } @Test public void testConstructor() throws NoSuchMethodException { Constructor constructor = validatorForTest.getConstructor(String.class, int.class); MatcherAssert.assertThat(parameterNameProvider.getParameterNames(constructor), Matchers.contains("grade", "number")); constructor = validatorForTest.getConstructor(); Assertions.assertTrue(parameterNameProvider.getParameterNames(constructor).isEmpty()); } } ================================================ FILE: swagger/swagger-invocation/pom.xml ================================================ 4.0.0 org.apache.servicecomb swagger 3.4.0-SNAPSHOT swagger-invocation Java Chassis::Swagger::Invocation pom invocation-core invocation-jaxrs invocation-springmvc invocation-validator ================================================ FILE: tracing/README.md ================================================ # Customized Tracing Customized tracing with [Zipkin](http://zipkin.io/) is supported to allow users to add tracing spans at points of interest. ## Set Up Customized Tracing 1. Include the following dependency ``` org.apache.servicecomb tracing-zipkin ``` 2. Enable tracing with annotation `@EnableZipkinTracing` on your application entry or configuration Import `tracing.zipkin.EnableZipkinTracing` package ``` import org.apache.servicecomb.tracing.zipkin.EnableZipkinTracing; ``` Add annotation `@EnableZipkinTracing` ``` @SpringBootApplication @EnableZipkinTracing public class ZipkinSpanTestApplication { public static void main(String[] args) { SpringApplication.run(ZipkinSpanTestApplication.class); } } ``` 3. Add new span to the point of interest with annotation `@Span` Import `tracing.Span` package ``` import org.apache.servicecomb.tracing.Span; ``` Add annotation `@Span` ``` @Component public class SlowRepoImpl implements SlowRepo { private static final Logger logger = LoggerFactory.getLogger(SlowRepoImpl.class); private final Random random = new Random(); @Span @Override public String crawl() throws InterruptedException { logger.info("in /crawl"); Thread.sleep(random.nextInt(200)); return "crawled"; } } ``` That's it! ## Reported Span Data Customized tracing span includes two pieces of data: * span name - annotated method name * call.path - annotated method signature e.g. the example `SlowRepoImpl` in the previous section reports the following span | key | value | | --- | --- | | span name | crawl | | call.path | public abstract java.lang.String org.apache.servicecomb.tests.tracing.SlowRepo.crawl() throws java.lang.InterruptedException | ## Constraints * Customized tracing with annotation only supports method calls in the request thread. * Classes with `@Span` must be a spring managed bean. If you want to do load-time weaving for non spring beans, you have to do it manually according to this [answer](https://stackoverflow.com/questions/41383941/load-time-weaving-for-non-spring-beans-in-a-spring-application). ================================================ FILE: tracing/pom.xml ================================================ org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default 4.0.0 tracing Java Chassis::Tracing pom tracing-common tracing-zipkin org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-logging ================================================ FILE: tracing/tracing-common/pom.xml ================================================ tracing org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 tracing-common Java Chassis::Tracing::Common ================================================ FILE: tracing/tracing-common/src/main/java/org/apache/servicecomb/tracing/Span.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Span { String spanName() default ""; String callPath() default ""; } ================================================ FILE: tracing/tracing-zipkin/pom.xml ================================================ tracing org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 tracing-zipkin Java Chassis::Tracing::Zipkin org.apache.servicecomb foundation-common org.apache.servicecomb handler-tracing-zipkin org.apache.servicecomb tracing-common io.zipkin.brave brave-spring-beans io.zipkin.brave brave io.zipkin.brave brave-context-slf4j io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.reporter2 zipkin-reporter-brave ================================================ FILE: tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/EnableZipkinTracing.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /** * This annotation enables auto-configuration of Zipkin for auto span generation using annotation {@link org.apache.servicecomb.tracing.Span}. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(ZipkinSpanAspectConfig.class) public @interface EnableZipkinTracing { } ================================================ FILE: tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspect.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import org.apache.servicecomb.tracing.Span; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brave.Tracing; @Aspect class ZipkinSpanAspect { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private final ZipkinTracingAdviser adviser; ZipkinSpanAspect(Tracing tracing) { this.adviser = new ZipkinTracingAdviser(tracing.tracer()); } @Around("execution(@org.apache.servicecomb.tracing.Span * *(..)) && @annotation(spanAnnotation)") public Object advise(ProceedingJoinPoint joinPoint, Span spanAnnotation) throws Throwable { String spanName = spanAnnotation.spanName(); String callPath = spanAnnotation.callPath(); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); LOG.debug("Generating zipkin span for method {}", method.toString()); if ("".equals(spanName)) { spanName = method.getName(); } if ("".equals(callPath)) { callPath = method.toString(); } return adviser.invoke(spanName, callPath, joinPoint::proceed); } } ================================================ FILE: tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import brave.Tracing; @Configuration @EnableAspectJAutoProxy class ZipkinSpanAspectConfig { @Bean ZipkinSpanAspect zipkinSpanAspect(Tracing tracing) { return new ZipkinSpanAspect(tracing); } } ================================================ FILE: tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; import brave.Span; import brave.Tracer; import brave.Tracer.SpanInScope; class ZipkinTracingAdviser { static final String CALL_PATH = "call.path"; private final Tracer tracer; ZipkinTracingAdviser(Tracer tracer) { this.tracer = tracer; } @SuppressWarnings({"unused", "try"}) T invoke(String spanName, String path, ThrowableSupplier supplier) throws Throwable { Span span = createSpan(spanName, path); try (SpanInScope spanInScope = tracer.withSpanInScope(span)) { return supplier.get(); } catch (Throwable throwable) { span.tag("error", throwable.getClass().getSimpleName() + ": " + throwable.getMessage()); throw throwable; } finally { span.finish(); } } private Span createSpan(String spanName, String path) { InvocationContext context = ContextUtils.getInvocationContext(); Span currentSpan = null; if (context != null) { currentSpan = context.getLocalContext(ZipkinTracingFilter.CONTEXT_TRACE_SPAN); } if (currentSpan == null) { currentSpan = tracer.currentSpan(); } if (currentSpan != null) { return tracer.newChild(currentSpan.context()).name(spanName).tag(CALL_PATH, path).start(); } return tracer.newTrace().name(spanName).tag(CALL_PATH, path).start(); } interface ThrowableSupplier { T get() throws Throwable; } } ================================================ FILE: tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.CALL_PATH; import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.stream.Collectors; import org.apache.servicecomb.tracing.zipkin.ZipkinSpanAspectTest.TracingConfig; import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication; import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication.CustomSpanTask; import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication.SomeSlowTask; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import brave.Tracing; import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; import zipkin2.Span; import zipkin2.reporter.brave.ZipkinSpanHandler; @SpringBootTest(classes = {ZipkinSpanTestApplication.class, TracingConfig.class}) public class ZipkinSpanAspectTest { private Queue spans; private SomeSlowTask someSlowTask; private CustomSpanTask customSpanTask; private Tracing tracing; @Autowired public void setSpans(Queue spans) { this.spans = spans; } @Autowired public void setSomeSlowTask(SomeSlowTask someSlowTask) { this.someSlowTask = someSlowTask; } @Autowired public void setCustomSpanTask(CustomSpanTask customSpanTask) { this.customSpanTask = customSpanTask; } @Autowired public void setTracing(Tracing tracing) { this.tracing = tracing; } public ZipkinSpanAspectTest() { } @AfterEach public void tearDown() throws Exception { tracing.close(); } @Test public void reportedSpanContainsAnnotatedMethodInfo() throws Exception { someSlowTask.crawl(); await().atMost(2, SECONDS).until(() -> !spans.isEmpty()); zipkin2.Span span = spans.poll(); MatcherAssert.assertThat(span.name(), is("crawl")); MatcherAssert.assertThat(tracedValues(span), contains(SomeSlowTask.class.getMethod("crawl").toString())); } @Test public void reportCustomSpanInformation() { customSpanTask.invoke(); await().atMost(2, SECONDS).until(() -> !spans.isEmpty()); zipkin2.Span span = spans.poll(); MatcherAssert.assertThat(span.name(), is("transaction1")); MatcherAssert.assertThat(tracedValues(span), contains("startA")); } private List tracedValues(zipkin2.Span spans) { return spans.tags().entrySet().stream() .filter(span -> CALL_PATH.equals(span.getKey()) || "error".equals(span.getKey())) .filter(span -> span.getValue() != null) .map(Map.Entry::getValue) .distinct() .collect(Collectors.toList()); } @Configuration static class TracingConfig { @Bean Queue spans() { return new ConcurrentLinkedDeque<>(); } @Bean @Primary Tracing zipkinTracing(Queue spans) { return Tracing.newBuilder() .currentTraceContext( ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(StrictScopeDecorator.create()).build()) .addSpanHandler(ZipkinSpanHandler.create(spans::add)) .build(); } } } ================================================ FILE: tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin; import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.CALL_PATH; import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import java.lang.reflect.Array; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executors; import java.util.stream.Collectors; import org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.ThrowableSupplier; import org.hamcrest.MatcherAssert; import brave.Span; import brave.Tracer.SpanInScope; import brave.Tracing; import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import zipkin2.reporter.brave.ZipkinSpanHandler; public class ZipkinTracingAdviserTest { private static final int nThreads = 10; private final String spanName = "some span"; private final String path = this.getClass().getCanonicalName(); private final String expected = "supplied"; private final RuntimeException error = new RuntimeException("oops"); private final ThrowableSupplier supplier = () -> expected; private final Map> traces = new ConcurrentHashMap<>(); private final Tracing tracing = Tracing.newBuilder() .currentTraceContext( ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(StrictScopeDecorator.create()).build()) .addSpanHandler(ZipkinSpanHandler.create( e -> traces.computeIfAbsent(e.traceId(), id -> new ConcurrentLinkedDeque<>()).add(e))) .build(); private final ZipkinTracingAdviser tracingAdviser = new ZipkinTracingAdviser(tracing.tracer()); @AfterEach public void tearDown() throws Exception { tracing.close(); } @Test public void startsNewRootSpan() throws Throwable { String result = tracingAdviser.invoke(spanName, path, supplier); MatcherAssert.assertThat(result, is(expected)); await().atMost(2, SECONDS).until(() -> !traces.isEmpty()); zipkin2.Span span = traces.values().iterator().next().poll(); MatcherAssert.assertThat(span.name(), is(spanName)); MatcherAssert.assertThat(tracedValues(span), contains(this.getClass().getCanonicalName())); } @Test public void includesExceptionInTags() throws Throwable { try { tracingAdviser.invoke(spanName, path, () -> { throw error; }); expectFailing(RuntimeException.class); } catch (RuntimeException ignored) { } await().atMost(2, SECONDS).until(() -> !traces.isEmpty()); zipkin2.Span span = traces.values().iterator().next().poll(); Assertions.assertEquals(spanName, span.name()); MatcherAssert.assertThat(tracedValues(span), containsInAnyOrder(this.getClass().getCanonicalName(), "RuntimeException: oops")); } @SuppressWarnings({"unused", "try"}) @Test public void startsNewChildSpan() { CyclicBarrier cyclicBarrier = new CyclicBarrier(nThreads); CompletableFuture[] futures = (CompletableFuture[]) Array.newInstance(CompletableFuture.class, nThreads); for (int i = 0; i < nThreads; i++) { futures[i] = CompletableFuture.runAsync(() -> { Span currentSpan = tracing.tracer().newTrace().start(); waitTillAllAreReady(cyclicBarrier); try (SpanInScope spanInScope = tracing.tracer().withSpanInScope(currentSpan)) { MatcherAssert.assertThat(tracingAdviser.invoke(spanName, path, supplier), is(expected)); } catch (Throwable throwable) { Assertions.fail(throwable.getMessage()); } finally { currentSpan.finish(); } }, Executors.newFixedThreadPool(nThreads)); } CompletableFuture.allOf(futures).join(); MatcherAssert.assertThat(traces.size(), is(nThreads)); for (Queue queue : traces.values()) { zipkin2.Span child = queue.poll(); MatcherAssert.assertThat(child.name(), is(spanName)); zipkin2.Span parent = queue.poll(); MatcherAssert.assertThat(child.parentId(), is(parent.id())); MatcherAssert.assertThat(child.traceId(), is(parent.traceId())); MatcherAssert.assertThat(tracedValues(child), contains(this.getClass().getCanonicalName())); } } private void waitTillAllAreReady(CyclicBarrier cyclicBarrier) { try { cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { throw new RuntimeException(e); } } private List tracedValues(zipkin2.Span spans) { return spans.tags().entrySet().stream() .filter(span -> CALL_PATH.equals(span.getKey()) || "error".equals(span.getKey())) .filter(span -> span.getValue() != null) .map(Map.Entry::getValue) .distinct() .collect(Collectors.toList()); } } ================================================ FILE: tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/app/ZipkinSpanTestApplication.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.tracing.zipkin.app; import org.apache.servicecomb.tracing.Span; import org.apache.servicecomb.tracing.zipkin.EnableZipkinTracing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableZipkinTracing public class ZipkinSpanTestApplication { public static void main(String[] args) { SpringApplication.run(ZipkinSpanTestApplication.class); } @Bean SomeSlowTask someSlowTask() { return new SomeSlowTask(); } @Bean CustomSpanTask customSpanTask() { return new CustomSpanTask(); } public static class SomeSlowTask { @Span public String crawl() { return "crawling..."; } } public static class CustomSpanTask { @Span(spanName = "transaction1", callPath = "startA") public String invoke() { return "invoke the method"; } } } ================================================ FILE: transports/pom.xml ================================================ 4.0.0 org.apache.servicecomb java-chassis-parent 3.4.0-SNAPSHOT ../parents/default transports Java Chassis::Transports pom transport-highway transport-rest transport-common ================================================ FILE: transports/transport-common/pom.xml ================================================ transports org.apache.servicecomb 3.4.0-SNAPSHOT 4.0.0 transport-common Java Chassis::Transports::common org.apache.servicecomb foundation-config org.apache.servicecomb foundation-test-scaffolding org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test ================================================ FILE: transports/transport-common/src/main/java/org/apache/servicecomb/transport/common/TransportConfigUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.common; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class TransportConfigUtils { private static final Logger LOGGER = LoggerFactory.getLogger(TransportConfigUtils.class); private TransportConfigUtils() { } // old verticle count key is ambiguous // suggest to use new name public static int readVerticleCount(String key, String deprecatedKey) { int count = LegacyPropertyFactory.getIntProperty(key, -1); if (count > 0) { return count; } count = LegacyPropertyFactory.getIntProperty(deprecatedKey, -1); if (count > 0) { LOGGER.warn("{} is ambiguous, and deprecated, recommended to use {}.", deprecatedKey, key); return count; } // default value count = Math.min(Runtime.getRuntime().availableProcessors(), 8); LOGGER.info("{} not defined, set to {}.", key, count); return count; } } ================================================ FILE: transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.common; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import mockit.Mock; import mockit.MockUp; public class TestTransportConfigUtils { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setup() { LegacyPropertyFactory.setEnvironment(environment); } @AfterAll public static void teardown() { } static String key = "verticle-count"; static String deprecatedKey = "thread-count"; @Test public void readVerticleCount_new_exist() { Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(10); Assertions.assertEquals(10, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); } @Test public void readVerticleCount_old_exist() { Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty("thread-count", int.class, -1)).thenReturn(10); LogCollector collector = new LogCollector(); Assertions.assertEquals(10, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); Assertions.assertEquals("thread-count is ambiguous, and deprecated, recommended to use verticle-count.", collector.getEvent(0).getMessage().getFormattedMessage()); collector.tearDown(); } @Test public void readVerticleCount_default_smallCpu() { Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty("thread-count", int.class, -1)).thenReturn(-1); new MockUp() { @Mock int availableProcessors() { return 7; } }; LogCollector collector = new LogCollector(); Assertions.assertEquals(7, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); Assertions.assertEquals("verticle-count not defined, set to 7.", collector.getLastEvents().getMessage().getFormattedMessage()); collector.tearDown(); } @Test public void readVerticleCount_default_bigCpu() { Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(-1); Mockito.when(environment.getProperty("thread-count", int.class, -1)).thenReturn(-1); AtomicInteger count = new AtomicInteger(8); new MockUp() { @Mock int availableProcessors() { return count.get(); } }; LogCollector collector = new LogCollector(); Assertions.assertEquals(8, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); Assertions.assertEquals("verticle-count not defined, set to 8.", collector.getLastEvents().getMessage().getFormattedMessage()); count.set(9); collector.clear(); Assertions.assertEquals(8, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); Assertions.assertEquals("verticle-count not defined, set to 8.", collector.getLastEvents().getMessage().getFormattedMessage()); collector.tearDown(); } } ================================================ FILE: transports/transport-common/src/test/resources/log4j2.xml ================================================ ================================================ FILE: transports/transport-highway/pom.xml ================================================ 4.0.0 org.apache.servicecomb transports 3.4.0-SNAPSHOT transport-highway Java Chassis::Transports::Hightway org.apache.servicecomb java-chassis-core org.apache.servicecomb common-protobuf org.apache.servicecomb foundation-vertx org.apache.servicecomb foundation-metrics org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb transport-common io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.jmockit jmockit test ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.util.Map; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; import com.google.common.annotations.VisibleForTesting; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; public class HighwayClient { private static final String SSL_KEY = "highway.consumer"; private ClientPoolManager clientMgr; public void init(Vertx vertx) throws Exception { TcpClientConfig normalConfig = createTcpClientConfig(); normalConfig.setSsl(false); TcpClientConfig sslConfig = createTcpClientConfig(); sslConfig.setSsl(true); clientMgr = new ClientPoolManager<>(vertx, new HighwayClientPoolFactory(normalConfig, sslConfig)); DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientMgr, HighwayConfig.getClientThreadCount()); Map result = VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); if (!(boolean) result.get("code")) { throw new IllegalStateException((String) result.get("message")); } } @VisibleForTesting TcpClientConfig createTcpClientConfig() { TcpClientConfig tcpClientConfig = new TcpClientConfig(); // global request timeout to be login timeout tcpClientConfig.setMsLoginTimeout( LegacyPropertyFactory.getLongProperty("servicecomb.request.timeout", TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)); SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(SSL_KEY, LegacyPropertyFactory.getEnvironment()); SSLOption sslOption; if (factory == null) { sslOption = SSLOption.build(SSL_KEY, LegacyPropertyFactory.getEnvironment()); } else { sslOption = factory.createSSLOption(); } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); VertxTLSBuilder.buildClientOptionsBase(sslOption, sslCustom, tcpClientConfig); if (!sslOption.isCheckCNHost()) { tcpClientConfig.setHostnameVerificationAlgorithm(""); } else { tcpClientConfig.setHostnameVerificationAlgorithm("HTTPS"); } return tcpClientConfig; } public HighwayClientPackage createClientPackage(Invocation invocation, OperationProtobuf operationProtobuf) { long msRequestTimeout = invocation.getOperationMeta().getConfig().getMsRequestTimeout(); return new HighwayClientPackage(invocation, operationProtobuf, msRequestTimeout); } public HighwayClientConnection findClientPool(Invocation invocation) { return clientMgr.findClientPool(invocation.isSync()) .findOrCreateClient(invocation.getEndpoint().getEndpoint()); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; import org.apache.servicecomb.foundation.vertx.client.tcp.NetClientWrapper; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConnection; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.apache.servicecomb.transport.highway.message.LoginRequest; import org.apache.servicecomb.transport.highway.message.LoginResponse; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.Context; import io.vertx.core.buffer.Buffer; public class HighwayClientConnection extends TcpClientConnection { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientConnection.class); public HighwayClientConnection(Context context, NetClientWrapper netClientWrapper, String endpoint) { super(context, netClientWrapper, endpoint); setLocalSupportLogin(true); } @Override protected TcpOutputStream createLogin() { try { RequestHeader header = new RequestHeader(); header.setMsgType(MsgType.LOGIN); LoginRequest login = new LoginRequest(); login.setProtocol(CoreConst.HIGHWAY); HighwayOutputStream os = new HighwayOutputStream(AbstractTcpClientPackage.getAndIncRequestId()); os.write(header, LoginRequest.getRootSerializer(), login); return os; } catch (Throwable e) { throw new Error("impossible.", e); } } @Override protected boolean onLoginResponse(Buffer bodyBuffer) { try { LoginResponse.readObject(bodyBuffer); return true; } catch (Throwable e) { LOGGER.error("decode login response failed.", e); return false; } } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnectionPool.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientConnectionPool; import org.apache.servicecomb.foundation.vertx.client.tcp.NetClientWrapper; import io.vertx.core.Context; public class HighwayClientConnectionPool extends AbstractTcpClientConnectionPool { public HighwayClientConnectionPool(Context context, NetClientWrapper netClientWrapper) { super(context, netClientWrapper); } @Override protected HighwayClientConnection create(String endpoint) { return new HighwayClientConnection(context, netClientWrapper, endpoint); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpData; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HighwayClientFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientFilter.class); public static final String NAME = "highway-client"; @Override public String getName() { return NAME; } @Override public boolean enabledForTransport(String transport) { return CoreConst.HIGHWAY.equals(transport); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { LOGGER.debug("Sending request by highway, operation={}, endpoint={}.", invocation.getMicroserviceQualifiedName(), invocation.getEndpoint().getEndpoint()); OperationProtobuf operationProtobuf = ProtobufManager.getOrCreateOperation(invocation); return send(invocation, operationProtobuf) .thenApply(tcpData -> convertToResponse(invocation, operationProtobuf, tcpData)) .thenApply(this::convertFailedResponseToException); } protected CompletableFuture send(Invocation invocation, OperationProtobuf operationProtobuf) { invocation.getInvocationStageTrace().startConsumerConnection(); HighwayClient highwayClient = ((HighwayTransport) invocation.getTransport()).getHighwayClient(); HighwayClientPackage clientPackage = highwayClient.createClientPackage(invocation, operationProtobuf); HighwayClientConnection clientConnection = highwayClient.findClientPool(invocation); invocation.getInvocationStageTrace().finishConsumerConnection(); invocation.onStartSendRequest(); invocation.getInvocationStageTrace().startConsumerSendRequest(); CompletableFuture sendFuture = clientConnection .send(clientPackage).whenComplete((r, e) -> invocation.getInvocationStageTrace().finishWaitResponse()); invocation.getInvocationStageTrace().finishConsumerSendRequest(); invocation.getInvocationStageTrace().startWaitResponse(); return invocation.optimizeSyncConsumerThread(sendFuture); } protected Response convertToResponse(Invocation invocation, OperationProtobuf operationProtobuf, TcpData tcpData) { try { invocation.getInvocationStageTrace().startConsumerDecodeResponse(); Response result = HighwayCodec.decodeResponse(invocation, operationProtobuf, tcpData); invocation.getInvocationStageTrace().finishConsumerDecodeResponse(); return result; } catch (Exception e) { throw AsyncUtils.rethrow(e); } } protected Response convertFailedResponseToException(Response response) { if (response.isFailed()) { Object errorData = response.getResult(); if (errorData instanceof InvocationException) { errorData = ((InvocationException) errorData).getErrorData(); } throw Exceptions.create(response.getStatus(), errorData); } return response; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPackage.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HighwayClientPackage extends AbstractTcpClientPackage { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientPackage.class); private final Invocation invocation; private final OperationProtobuf operationProtobuf; public HighwayClientPackage(Invocation invocation, OperationProtobuf operationProtobuf, long msRequestTimeout) { this.invocation = invocation; this.operationProtobuf = operationProtobuf; this.setMsRequestTimeout(msRequestTimeout); } @Override public TcpOutputStream createStream() { try { return HighwayCodec.encodeRequest(msgId, invocation, operationProtobuf); } catch (Exception e) { String msg = String.format("encode request failed. appid=%s, qualifiedName=%s", invocation.getAppId(), invocation.getOperationMeta().getMicroserviceQualifiedName()); LOGGER.error(msg, e); throw new Error(msg, e); } } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPoolFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPoolFactory; import org.apache.servicecomb.foundation.vertx.client.tcp.NetClientWrapper; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; import io.vertx.core.Context; public class HighwayClientPoolFactory extends AbstractTcpClientPoolFactory { public HighwayClientPoolFactory(TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { super(normalClientConfig, sslClientConfig); } @Override protected HighwayClientConnectionPool doCreateClientPool(Context context, NetClientWrapper netClientWrapper) { return new HighwayClientConnectionPool(context, netClientWrapper); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayCodec.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootDeserializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpData; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.apache.servicecomb.transport.highway.message.ResponseHeader; import com.fasterxml.jackson.databind.JavaType; import com.google.common.base.Defaults; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.vertx.core.buffer.Buffer; @SuppressWarnings("rawtypes") public final class HighwayCodec { private HighwayCodec() { } public static TcpOutputStream encodeRequest(long msgId, Invocation invocation, OperationProtobuf operationProtobuf) throws Exception { // 写header RequestHeader header = new RequestHeader(); header.setMsgType(MsgType.REQUEST); header.setFlags(0); header.setDestMicroservice(invocation.getMicroserviceName()); header.setSchemaId(invocation.getSchemaId()); header.setOperationName(invocation.getOperationName()); header.setContext(invocation.getContext()); HighwayOutputStream os = new HighwayOutputStream(msgId); os.write(header, operationProtobuf.getRequestRootSerializer(), invocation.getSwaggerArguments()); return os; } // Proto buffer never serialize default values, put it back in provider. // Or will get IllegalArgumentsException for primitive types. private static Map addPrimitiveTypeDefaultValues(Invocation invocation, Map swaggerArguments) { if (invocation.getOperationMeta().getSwaggerProducerOperation() != null && !invocation.isEdge()) { List swaggerParameters = invocation.getOperationMeta().getSwaggerOperation() .getParameters(); if (swaggerParameters != null) { for (Parameter parameter : swaggerParameters) { if (swaggerArguments.get(parameter.getName()) == null) { Type type = invocation.getOperationMeta().getSwaggerProducerOperation() .getSwaggerParameterType(parameter.getName()); swaggerArguments.put(parameter.getName(), defaultPrimitiveValue(null, type)); } } } RequestBody requestBody = invocation.getOperationMeta().getSwaggerOperation().getRequestBody(); if (requestBody != null && requestBody.getContent() != null && requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null && requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema() != null && requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties() != null) { for (Object entry : requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().entrySet()) { Entry types = (Entry) entry; if (swaggerArguments.get(types.getKey()) == null) { Type type = invocation.getOperationMeta().getSwaggerProducerOperation() .getSwaggerParameterType(types.getKey()); swaggerArguments.put(types.getKey(), defaultPrimitiveValue(null, type)); } } } } return swaggerArguments; } public static void decodeRequest(Invocation invocation, RequestHeader header, OperationProtobuf operationProtobuf, Buffer bodyBuffer) throws Exception { RequestRootDeserializer requestDeserializer = operationProtobuf.getRequestRootDeserializer(); Map swaggerArguments = requestDeserializer.deserialize(bodyBuffer.getBytes()); addPrimitiveTypeDefaultValues(invocation, swaggerArguments); invocation.setSwaggerArguments(swaggerArguments); } public static RequestHeader readRequestHeader(Buffer headerBuffer) throws Exception { return RequestHeader.readObject(headerBuffer); } public static Buffer encodeResponse(long msgId, ResponseHeader header, ResponseRootSerializer bodySchema, Object body) throws Exception { try (HighwayOutputStream os = new HighwayOutputStream(msgId)) { os.write(header, bodySchema, body); return os.getBuffer(); } } public static Response decodeResponse(Invocation invocation, OperationProtobuf operationProtobuf, TcpData tcpData) throws Exception { ResponseHeader header = ResponseHeader.readObject(tcpData.getHeaderBuffer()); if (header.getContext() != null) { invocation.getContext().putAll(header.getContext()); } ResponseRootDeserializer bodySchema = operationProtobuf .findResponseRootDeserializer(header.getStatusCode()); JavaType type = invocation.findResponseType(header.getStatusCode()); Object body = bodySchema .deserialize(tcpData.getBodyBuffer().getBytes(), type); Response response = Response.create(header.getStatusCode(), header.getReasonPhrase() , defaultPrimitiveValue(body, type)); response.setHeaders(header.toMultiMap()); return response; } private static Object defaultPrimitiveValue(Object body, Type type) { if (body == null) { if (type instanceof Class && ((Class) type).isPrimitive()) { return Defaults.defaultValue((Class) type); } if (type instanceof JavaType && ((JavaType) type).isPrimitive()) { return Defaults.defaultValue(((JavaType) type).getRawClass()); } } return body; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.transport.common.TransportConfigUtils; public final class HighwayConfig { private HighwayConfig() { } public static String getAddress() { return LegacyPropertyFactory.getStringProperty("servicecomb.highway.address", null); } public static int getServerThreadCount() { return TransportConfigUtils.readVerticleCount( "servicecomb.highway.server.verticle-count", "servicecomb.highway.server.thread-count"); } public static int getClientThreadCount() { return TransportConfigUtils.readVerticleCount( "servicecomb.highway.client.verticle-count", "servicecomb.highway.client.thread-count"); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayOutputStream.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.apache.servicecomb.transport.highway.message.ResponseHeader; public class HighwayOutputStream extends TcpOutputStream { public HighwayOutputStream(long msgId) { super(msgId); } public void write(RequestHeader header, RequestRootSerializer requestRootSerializer, Object body) throws Exception { write(RequestHeader.getRootSerializer().serialize(header), requestRootSerializer.serialize(body)); } public void write(ResponseHeader header, ResponseRootSerializer responseRootSerializer, Object body) throws Exception { write(ResponseHeader.getRootSerializer().serialize(header), responseRootSerializer.serialize(body)); } public void write(RequestHeader header, RootSerializer bodySerializer, Object body) throws Exception { write(RequestHeader.getRootSerializer(), header, bodySerializer, body); } public void write(ResponseHeader header, RootSerializer bodySerializer, Object body) throws Exception { write(ResponseHeader.getRootSerializer(), header, bodySerializer, body); } public void write(RootSerializer headerSerializer, Object header, RootSerializer bodySerializer, Object body) throws Exception { byte[] headerBytes = new byte[0]; byte[] bodyBytes = new byte[0]; if (headerSerializer != null) { headerBytes = headerSerializer.serialize(header); } if (bodySerializer != null) { bodyBytes = bodySerializer.serialize(body); } write(headerBytes, bodyBytes); } private void write(byte[] headerBytes, byte[] bodyBytes) throws Exception { int headerLength = 0; int totalLength = 0; if (headerBytes != null) { headerLength = headerBytes.length; totalLength = totalLength + headerLength; } if (bodyBytes != null) { totalLength = totalLength + bodyBytes.length; } this.writeLength(totalLength, headerLength); this.write(headerBytes); this.write(bodyBytes); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HighwayProducerInvocationFlow extends ProducerInvocationFlow { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayProducerInvocationFlow.class); private final TcpConnection connection; private final long msgId; public HighwayProducerInvocationFlow(InvocationCreator invocationCreator, TcpConnection connection, long msgId) { super(invocationCreator); this.connection = connection; this.msgId = msgId; } @Override protected Invocation sendCreateInvocationException(Throwable throwable) { logException(throwable); return null; } private void logException(Throwable throwable) { if (Exceptions.isPrintInvocationStackTrace()) { LOGGER.error("Failed to prepare invocation, msgId={}.", msgId, throwable); return; } LOGGER.error("Failed to prepare invocation, msgId={}, message={}.", msgId, ExceptionUtils.getExceptionMessageWithoutTrace(throwable)); } @Override protected void endResponse(Invocation invocation, Response response) { HighwayTransportContext transportContext = invocation.getTransportContext(); connection.write(transportContext.getResponseBuffer()); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServer.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.server.TcpServer; import org.apache.servicecomb.foundation.vertx.server.TcpServerConnection; public class HighwayServer extends TcpServer { private final Endpoint endpoint; public HighwayServer(Endpoint endpoint) { super((URIEndpointObject) endpoint.getAddress()); this.endpoint = endpoint; } @Override protected TcpServerConnection createTcpServerConnection() { return new HighwayServerConnection(endpoint); } @Override protected int getConnectionLimit() { return LegacyPropertyFactory.getIntProperty("servicecomb.highway.server.connection-limit", Integer.MAX_VALUE); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.apache.servicecomb.transport.highway.message.ResponseHeader; import io.vertx.core.buffer.Buffer; public class HighwayServerCodecFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "highway-server-codec"; @Override public String getName() { return NAME; } @Override public int getOrder() { // almost time, should be the first filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; } @Override public boolean enabledForTransport(String transport) { return CoreConst.HIGHWAY.equals(transport); } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return CompletableFuture.completedFuture(invocation) .thenCompose(this::decodeRequest) .thenCompose(nextNode::onFilter) .exceptionally(exception -> toProducerResponse(invocation, exception)) .thenCompose(response -> encodeResponse(invocation, response)); } protected CompletableFuture decodeRequest(Invocation invocation) { HighwayTransportContext transportContext = invocation.getTransportContext(); try { invocation.getInvocationStageTrace().startProviderDecodeRequest(); HighwayCodec.decodeRequest(invocation, transportContext.getHeader(), transportContext.getOperationProtobuf(), transportContext.getBodyBuffer()); invocation.getInvocationStageTrace().finishProviderDecodeRequest(); return CompletableFuture.completedFuture(invocation); } catch (Exception e) { return AsyncUtils.completeExceptionally(e); } } protected CompletableFuture encodeResponse(Invocation invocation, Response response) { invocation.onEncodeResponseStart(response); ResponseHeader header = new ResponseHeader(); header.setStatusCode(response.getStatusCode()); header.setReasonPhrase(response.getReasonPhrase()); header.setContext(invocation.getContext()); header.fromMultiMap(response.getHeaders()); HighwayTransportContext transportContext = invocation.getTransportContext(); long msgId = transportContext.getMsgId(); OperationProtobuf operationProtobuf = transportContext.getOperationProtobuf(); ResponseRootSerializer bodySchema = operationProtobuf.findResponseRootSerializer(response.getStatusCode()); try { boolean failed = response.getResult() instanceof InvocationException; Buffer respBuffer; if (failed) { respBuffer = HighwayCodec.encodeResponse( msgId, header, bodySchema, ((InvocationException) response.getResult()).getErrorData()); } else { respBuffer = HighwayCodec.encodeResponse( msgId, header, bodySchema, response.getResult()); } transportContext.setResponseBuffer(respBuffer); invocation.onEncodeResponseFinish(); return CompletableFuture.completedFuture(response); } catch (Exception e) { // keep highway performance and simple, this encoding/decoding error not need handle by client String msg = String.format("encode response failed, msgId=%d", msgId); return AsyncUtils.completeExceptionally(new IllegalStateException(msg, e)); } } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.util.concurrent.CompletableFuture; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.vertx.server.TcpBufferHandler; import org.apache.servicecomb.foundation.vertx.server.TcpParser; import org.apache.servicecomb.foundation.vertx.server.TcpServerConnection; import org.apache.servicecomb.transport.highway.message.LoginRequest; import org.apache.servicecomb.transport.highway.message.LoginResponse; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.apache.servicecomb.transport.highway.message.ResponseHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; public class HighwayServerConnection extends TcpServerConnection implements TcpBufferHandler { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerConnection.class); private final Endpoint endpoint; public HighwayServerConnection(Endpoint endpoint) { this.endpoint = endpoint; } @Override public void init(NetSocket netSocket) { splitter = new TcpParser(this); super.init(netSocket); } @Override public void handle(long msgId, Buffer headerBuffer, Buffer bodyBuffer) { RequestHeader requestHeader = decodeRequestHeader(msgId, headerBuffer); if (requestHeader == null) { return; } switch (requestHeader.getMsgType()) { case MsgType.REQUEST: onRequest(msgId, requestHeader, bodyBuffer); break; case MsgType.LOGIN: onLogin(msgId, requestHeader, bodyBuffer); break; default: throw new Error("Unknown tcp msgType " + requestHeader.getMsgType()); } } protected RequestHeader decodeRequestHeader(long msgId, Buffer headerBuffer) { try { return HighwayCodec.readRequestHeader(headerBuffer); } catch (Exception e) { String msg = String.format("decode request header error, msgId=%d", msgId); LOGGER.error(msg, e); netSocket.close(); return null; } } protected void onLogin(long msgId, RequestHeader header, Buffer bodyBuffer) { LoginRequest request; try { request = LoginRequest.readObject(bodyBuffer); } catch (Exception e) { String msg = String.format("decode setParameter error, msgId=%d", msgId); LOGGER.error(msg, e); netSocket.close(); return; } if (request != null) { this.setProtocol(request.getProtocol()); this.setZipName(request.getZipName()); } try (HighwayOutputStream os = new HighwayOutputStream(msgId)) { ResponseHeader responseHeader = new ResponseHeader(); responseHeader.setStatusCode(Status.OK.getStatusCode()); LoginResponse response = new LoginResponse(); os.write(ResponseHeader.getRootSerializer(), responseHeader, LoginResponse.getRootSerializer(), response); netSocket.write(os.getBuffer()); } catch (Exception e) { throw new Error("impossible.", e); } } protected void onRequest(long msgId, RequestHeader header, Buffer bodyBuffer) { InvocationCreator creator = () -> createInvocation(msgId, header, bodyBuffer); new HighwayProducerInvocationFlow(creator, this, msgId) .run(); } public CompletableFuture createInvocation(long msgId, RequestHeader header, Buffer bodyBuffer) { MicroserviceMeta microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); SchemaMeta schemaMeta = microserviceMeta.ensureFindSchemaMeta(header.getSchemaId()); OperationMeta operationMeta = schemaMeta.ensureFindOperation(header.getOperationName()); Invocation invocation = InvocationFactory.forProvider(endpoint, operationMeta, null); invocation.getHandlerContext().put(CoreConst.REMOTE_ADDRESS, netSocket.remoteAddress()); HighwayTransportContext transportContext = new HighwayTransportContext() .setConnection(this) .setMsgId(msgId) .setHeader(header) .setBodyBuffer(bodyBuffer) .setOperationProtobuf(ProtobufManager.getOrCreateOperation(invocation)); invocation.setTransportContext(transportContext); invocation.mergeContext(header.getContext()); return CompletableFuture.completedFuture(invocation); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.net.InetSocketAddress; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.AbstractVerticle; import io.vertx.core.Context; import io.vertx.core.Promise; import io.vertx.core.Vertx; public class HighwayServerVerticle extends AbstractVerticle { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerVerticle.class); public static final String SSL_KEY = "highway.provider"; private Endpoint endpoint; private URIEndpointObject endpointObject; @Override public void init(Vertx vertx, Context context) { super.init(vertx, context); this.endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); this.endpointObject = (URIEndpointObject) this.endpoint.getAddress(); } @Override public void start(Promise startPromise) throws Exception { try { super.start(); startListen(startPromise); } catch (Throwable e) { // vert.x got some states that not print error and execute call back in VertexUtils.blockDeploy, we add a log our self. LOGGER.error("", e); throw e; } } protected void startListen(Promise startPromise) { // if listen address is not provided, do not fail and maybe a consumer service. if (endpointObject == null) { LOGGER.warn("highway listen address is not configured, will not listen."); startPromise.complete(); return; } HighwayServer server = new HighwayServer(endpoint); server.init(vertx, SSL_KEY, ar -> { if (ar.succeeded()) { InetSocketAddress socketAddress = ar.result(); LOGGER.info("highway listen success. address={}:{}", socketAddress.getHostString(), socketAddress.getPort()); startPromise.complete(); return; } LOGGER.error(CoreConst.HIGHWAY, ar.cause()); startPromise.fail(ar.cause()); }); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.util.Collections; import java.util.Map; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.vertx.SimpleJsonObject; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.tcp.TcpConst; import io.vertx.core.DeploymentOptions; public class HighwayTransport extends AbstractTransport { private final HighwayClient highwayClient = new HighwayClient(); @Override public String getName() { return CoreConst.HIGHWAY; } @Override public boolean init() throws Exception { highwayClient.init(transportVertx); DeploymentOptions deployOptions = new DeploymentOptions().setInstances(HighwayConfig.getServerThreadCount()); setListenAddressWithoutSchema(HighwayConfig.getAddress(), Collections.singletonMap(TcpConst.LOGIN, "true")); SimpleJsonObject json = new SimpleJsonObject(); json.put(ENDPOINT_KEY, getEndpoint()); deployOptions.setConfig(json); deployOptions.setWorkerPoolName("pool-worker-transport-highway"); Map result = VertxUtils.blockDeploy(transportVertx, HighwayServerVerticle.class, deployOptions); if ((boolean) result.get("code")) { return true; } else { throw new IllegalStateException((String) result.get("message")); } } public HighwayClient getHighwayClient() { return highwayClient; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; import org.apache.servicecomb.transport.highway.message.RequestHeader; import io.vertx.core.Context; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; public class HighwayTransportContext implements VertxTransportContext { private final Context vertxContext; private TcpConnection connection; private long msgId; private RequestHeader header; private Buffer bodyBuffer; private OperationProtobuf operationProtobuf; private Buffer responseBuffer; public HighwayTransportContext() { this.vertxContext = Vertx.currentContext(); } @Override public Context getVertxContext() { return vertxContext; } public TcpConnection getConnection() { return connection; } public HighwayTransportContext setConnection(TcpConnection connection) { this.connection = connection; return this; } public long getMsgId() { return msgId; } public HighwayTransportContext setMsgId(long msgId) { this.msgId = msgId; return this; } public RequestHeader getHeader() { return header; } public HighwayTransportContext setHeader(RequestHeader header) { this.header = header; return this; } public Buffer getBodyBuffer() { return bodyBuffer; } public HighwayTransportContext setBodyBuffer(Buffer bodyBuffer) { this.bodyBuffer = bodyBuffer; return this; } public OperationProtobuf getOperationProtobuf() { return operationProtobuf; } public HighwayTransportContext setOperationProtobuf( OperationProtobuf operationProtobuf) { this.operationProtobuf = operationProtobuf; return this; } public Buffer getResponseBuffer() { return responseBuffer; } public HighwayTransportContext setResponseBuffer(Buffer responseBuffer) { this.responseBuffer = responseBuffer; return this; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/MsgType.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; public final class MsgType { public static final byte REQUEST = 0; public static final byte LOGIN = 1; private MsgType() { } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/TransportHighwayConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TransportHighwayConfiguration { @Bean public HighwayServerCodecFilter highwayServerCodecFilter() { return new HighwayServerCodecFilter(); } @Bean public HighwayClientFilter highwayClientFilter() { return new HighwayClientFilter(); } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/Headers.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Headers { private Map> headerMap; public Map> getHeaderMap() { return headerMap; } public void setHeaderMap(Map> headerMap) { this.headerMap = headerMap; } public Object getFirst(String name) { if (headerMap == null) { return null; } List values = headerMap.get(name); if (values == null || values.isEmpty()) { return null; } return values.get(0); } public List getHeader(String name) { if (headerMap == null) { return null; } return headerMap.get(name); } public Headers addHeader(String name, Object value) { if (headerMap == null) { headerMap = new HashMap<>(); } List values = headerMap.computeIfAbsent(name, k -> new ArrayList<>()); values.add(value); return this; } public Headers addHeader(String name, List value) { if (headerMap == null) { headerMap = new HashMap<>(); } List values = headerMap.computeIfAbsent(name, k -> new ArrayList<>()); values.addAll(value); return this; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import io.vertx.core.buffer.Buffer; public class LoginRequest { private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); private static final RootDeserializer rootDeserializer = protoMapperFactory .createFromName("LoginRequest.proto") .createRootDeserializer("LoginRequest", LoginRequest.class); private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("LoginRequest.proto") .createRootSerializer("LoginRequest", LoginRequest.class); public static RootSerializer getRootSerializer() { return rootSerializer; } public static LoginRequest readObject(Buffer bodyBuffer) throws Exception { return rootDeserializer.deserialize(bodyBuffer.getBytes()); } private String protocol; // 压缩算法名字 private String zipName; public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getZipName() { return zipName; } public void setZipName(String zipName) { this.zipName = zipName; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import io.vertx.core.buffer.Buffer; public class LoginResponse { private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); private static final RootDeserializer rootDeserializer = protoMapperFactory .createFromName("LoginResponse.proto") .createRootDeserializer("LoginResponse", LoginResponse.class); private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("LoginResponse.proto") .createRootSerializer("LoginResponse", LoginResponse.class); public static RootSerializer getRootSerializer() { return rootSerializer; } public static LoginResponse readObject(Buffer bodyBuffer) throws Exception { return rootDeserializer.deserialize(bodyBuffer.getBytes()); } private String protocol; // 压缩算法名字 private String zipName; public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getZipName() { return zipName; } public void setZipName(String zipName) { this.zipName = zipName; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/RequestHeader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import java.util.Map; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import io.vertx.core.buffer.Buffer; public class RequestHeader { private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); private static final RootDeserializer rootDeserializer = protoMapperFactory .createFromName("RequestHeader.proto") .createRootDeserializer("RequestHeader", RequestHeader.class); private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("RequestHeader.proto") .createRootSerializer("RequestHeader", RequestHeader.class); public static RootSerializer getRootSerializer() { return rootSerializer; } public static RequestHeader readObject(Buffer bodyBuffer) throws Exception { return rootDeserializer.deserialize(bodyBuffer.getBytes()); } private int msgType; // 运行时必须的数据,比如body是否压缩 // 预留特性选项 private int flags; private String destMicroservice; private String schemaId; private String operationName; private Map context; public int getMsgType() { return msgType; } public void setMsgType(int msgType) { this.msgType = msgType; } public String getDestMicroservice() { return destMicroservice; } public void setDestMicroservice(String destMicroservice) { this.destMicroservice = destMicroservice; } public int getFlags() { return flags; } public void setFlags(int flags) { this.flags = flags; } public String getSchemaId() { return schemaId; } public void setSchemaId(String schemaId) { this.schemaId = schemaId; } public String getOperationName() { return operationName; } public void setOperationName(String operationName) { this.operationName = operationName; } public Map getContext() { return context; } public void setContext(Map context) { this.context = context; } } ================================================ FILE: transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/ResponseHeader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; import org.apache.servicecomb.foundation.protobuf.RootDeserializer; import org.apache.servicecomb.foundation.protobuf.RootSerializer; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; public class ResponseHeader { private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); private static final RootDeserializer rootDeserializer = protoMapperFactory .createFromName("ResponseHeader.proto") .createRootDeserializer("ResponseHeader", ResponseHeader.class); private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("ResponseHeader.proto") .createRootSerializer("ResponseHeader", ResponseHeader.class); public static RootSerializer getRootSerializer() { return rootSerializer; } public static ResponseHeader readObject(Buffer bodyBuffer) throws Exception { return rootDeserializer.deserialize(bodyBuffer.getBytes()); } // 运行时必须的数据,比如body是否压缩 // 预留特性选项 private int flags; private int statusCode; private String reasonPhrase; private Map context; private Headers headers = new Headers(); public int getFlags() { return flags; } public void setFlags(int flags) { this.flags = flags; } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public String getReasonPhrase() { return reasonPhrase; } public void setReasonPhrase(String reason) { this.reasonPhrase = reason; } public Map getContext() { return context; } public void setContext(Map context) { this.context = context; } public Headers getHeaders() { return headers; } public void setHeaders(Headers headers) { this.headers = headers; } public void fromMultiMap(MultiMap multiMap) { if (multiMap == null) { return; } for (Entry entry : multiMap.entries()) { headers.addHeader(entry.getKey(), entry.getValue()); } } public MultiMap toMultiMap() { MultiMap multiMap = MultiMap.caseInsensitiveMultiMap(); Map> headerMap = headers.getHeaderMap(); if (headerMap == null) { return multiMap; } for (Entry> entry : headerMap.entrySet()) { String key = entry.getKey(); for (Object value : entry.getValue()) { multiMap.add(key, value.toString()); } } return multiMap; } } ================================================ FILE: transports/transport-highway/src/main/resources/LoginRequest.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; package org.apache.servicecomb.transport.highway.message; message LoginRequest { string protocol = 1; string zipName = 2; } ================================================ FILE: transports/transport-highway/src/main/resources/LoginResponse.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; package org.apache.servicecomb.transport.highway.message; message LoginResponse { string protocol = 1; string zipName = 2; } ================================================ FILE: transports/transport-highway/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.transport.highway.HighwayTransport ================================================ FILE: transports/transport-highway/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.transport.highway.TransportHighwayConfiguration ================================================ FILE: transports/transport-highway/src/main/resources/RequestHeader.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; package org.apache.servicecomb.transport.highway.message; message RequestHeader { string destMicroservice = 1; int32 msgType = 2; int32 flags = 3; string schemaId = 4; string operationName = 5; map context = 6; } ================================================ FILE: transports/transport-highway/src/main/resources/ResponseHeader.proto ================================================ /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ syntax = "proto3"; import "google/protobuf/any.proto"; package org.apache.servicecomb.transport.highway.message; message ResponseHeader { int32 flags = 1; int32 statusCode = 2; string reasonPhrase = 3; map context = 4; Headers headers = 5; } message Headers { map headerMap = 1; } //@WrapProperty message ListGoogle_protobuf_Any { repeated google.protobuf.Any value = 1; } ================================================ FILE: transports/transport-highway/src/main/resources/microservice.yaml ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- servicecomb-config-order: -500 servicecomb: filter-chains: transport: scb-consumer-transport: highway: highway-client scb-producer-transport: highway: highway-server-codec ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/common/MockUtil.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.common; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.transport.highway.HighwayCodec; import org.apache.servicecomb.transport.highway.HighwayConfig; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.mockito.Mockito; import io.vertx.core.buffer.Buffer; import mockit.Mock; import mockit.MockUp; public class MockUtil { private static final MockUtil instance = new MockUtil(); private MockUtil() { } public static MockUtil getInstance() { return instance; } public void mockHighwayConfig() { new MockUp() { @Mock String getAddress() { return "127.0.0.1"; } }; } public RequestHeader requestHeader = new RequestHeader(); public boolean decodeRequestSucc = true; public void mockHighwayCodec() { new MockUp() { @Mock RequestHeader readRequestHeader(Buffer headerBuffer) throws Exception { return requestHeader; } @Mock public Invocation decodeRequest(RequestHeader header, OperationProtobuf operationProtobuf, Buffer bodyBuffer) throws Exception { if (decodeRequestSucc) { return Mockito.mock(Invocation.class); } throw new Error("decode failed"); } }; } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.SCBStatus; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.apache.servicecomb.transport.highway.message.ResponseHeader; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.core.env.Environment; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.impl.SysProps; import io.vertx.core.json.Json; import mockit.Expectations; import mockit.Mocked; import mockit.Verifications; public class HighwayServerCodecFilterTest { HighwayServerCodecFilter codecFilter = new HighwayServerCodecFilter(); Invocation invocation; @Mocked Endpoint endpoint; @Mocked OperationMeta operationMeta; @Mocked HighwayTransportContext transportContext; MultiMap headers = MultiMap.caseInsensitiveMultiMap(); FilterNode nextNode = new FilterNode(new AbstractFilter() { @Override public String getName() { return "test"; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { Response response = Response.ok("ok"); response.setHeaders(headers); return CompletableFuture.completedFuture(response); } }); static SCBEngine engine; Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { Mockito.when(environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); engine = SCBBootstrap.createSCBEngineForTest(environment); engine.setStatus(SCBStatus.UP); invocation = InvocationFactory.forProvider(endpoint, operationMeta, null); } @After public void tearDown() { engine.destroy(); } private void mockDecodeRequestFail() throws Exception { new Expectations(invocation) { { invocation.getTransportContext(); result = transportContext; } }; new Expectations(HighwayCodec.class) { { HighwayCodec.decodeRequest(invocation, (RequestHeader) any, (OperationProtobuf) any, (Buffer) any); result = new RuntimeExceptionWithoutStackTrace("encode request failed"); } }; } @Test public void should_not_invoke_filter_when_decode_request_failed(@Mocked FilterNode nextNode) throws Exception { mockDecodeRequestFail(); codecFilter.onFilter(invocation, nextNode); new Verifications() { { nextNode.onFilter(invocation); times = 0; } }; } @Test public void should_convert_exception_to_response_when_decode_request_failed() throws Exception { mockDecodeRequestFail(); Response response = codecFilter.onFilter(invocation, nextNode).get(); assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); JSONAssert.assertEquals(Json.encode(((InvocationException) response.getResult()).getErrorData()), "{\"code\":\"SCB.50000000\",\"message\":\"Unexpected " + "exception when processing null. encode request failed\"}", false); } private void success_invocation() throws InterruptedException, ExecutionException { new Expectations(invocation) { { invocation.getTransportContext(); result = transportContext; } }; codecFilter.onFilter(invocation, nextNode).get(); } @Test public void should_encode_response_header(@Mocked ResponseHeader responseHeader) throws ExecutionException, InterruptedException { success_invocation(); new Verifications() { { MultiMap captureHeaders; responseHeader.fromMultiMap(captureHeaders = withCapture()); assertThat(captureHeaders).isSameAs(headers); } }; } @Test public void should_encode_response() throws ExecutionException, InterruptedException { success_invocation(); new Verifications() { { Buffer captureBuffer; transportContext.setResponseBuffer(captureBuffer = withCapture()); assertThat(captureBuffer).isNotNull(); } }; } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import mockit.Deencapsulation; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestHighwayClient { private static final String REQUEST_TIMEOUT_KEY = "servicecomb.request.timeout"; HighwayClient client = new HighwayClient(); Environment environment = Mockito.mock(Environment.class); static long nanoTime = 123; @BeforeClass public static void setup() { new MockUp() { @Mock long nanoTime() { return nanoTime; } }; } @AfterClass public static void teardown() { } @Before public void setUp() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty(REQUEST_TIMEOUT_KEY, long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) .thenReturn((long) 2000); Mockito.when(environment.getProperty("servicecomb.highway.client.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.highway.client.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.highway.server.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.highway.server.thread-count", int.class, -1)) .thenReturn(-1); } @Test public void testLoginTimeout(@Mocked Vertx vertx) { TcpClientConfig tcpClientConfig = client.createTcpClientConfig(); Assertions.assertEquals(2000, tcpClientConfig.getMsLoginTimeout()); } @Test public void testHighwayClientSSL(@Mocked Vertx vertx) throws Exception { new MockUp() { @Mock Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) { Map result = new HashMap<>(); result.put("code", true); return result; } }; client.init(vertx); ClientPoolManager clientMgr = Deencapsulation.getField(client, "clientMgr"); Assertions.assertSame(vertx, Deencapsulation.getField(clientMgr, "vertx")); } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import io.vertx.core.impl.SysProps; public class TestHighwayTransport { private static final Logger LOGGER = LoggerFactory.getLogger(TestHighwayTransport.class); Environment environment = Mockito.mock(Environment.class); @BeforeClass public static void setup() { VertxUtils.blockCloseVertxByName("transport"); Thread.getAllStackTraces().keySet().forEach(t -> LOGGER.info("before: {}", t.getName())); } @AfterClass public static void teardown() { VertxUtils.blockCloseVertxByName("transport"); Thread.getAllStackTraces().keySet().forEach(t -> LOGGER.info("after: {}", t.getName())); } @Before public void setUp() { Mockito.when(environment.getProperty( "servicecomb.request.timeout", long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) .thenReturn((long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT); Mockito.when(environment.getProperty("servicecomb.highway.client.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.highway.client.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.highway.server.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.highway.server.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } @Test public void testGetInstance() { HighwayTransport transport = new HighwayTransport(); Assertions.assertNotNull(transport); } @Test public void testInit() { HighwayTransport transport = new HighwayTransport(); transport.setEnvironment(environment); boolean status = true; try { transport.init(); } catch (Exception e) { e.printStackTrace(); status = false; } Assertions.assertTrue(status); } @Test public void testHighway() { HighwayTransport transport = new HighwayTransport(); Invocation invocation = Mockito.mock(Invocation.class); commonHighwayMock(invocation); Assertions.assertEquals("highway", transport.getName()); } private void commonHighwayMock(Invocation invocation) { OperationMeta operationMeta = Mockito.mock(OperationMeta.class); Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); OperationProtobuf operationProtobuf = Mockito.mock(OperationProtobuf.class); Mockito.when(operationMeta.getExtData("protobuf")).thenReturn(operationProtobuf); Endpoint lEndpoint = Mockito.mock(Endpoint.class); Mockito.when(invocation.getEndpoint()).thenReturn(lEndpoint); RequestRootSerializer lWrapSchema = Mockito.mock(RequestRootSerializer.class); Mockito.when(operationProtobuf.getRequestRootSerializer()).thenReturn(lWrapSchema); URIEndpointObject ep = Mockito.mock(URIEndpointObject.class); Mockito.when(lEndpoint.getAddress()).thenReturn(ep); Mockito.when(ep.getHostOrIp()).thenReturn("127.0.0.1"); Mockito.when(ep.getPort()).thenReturn(80); } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.transport.common.MockUtil; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import io.vertx.core.Context; import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import mockit.Expectations; import mockit.Mocked; public class TestHighwayVerticle { @Test public void testHighwayVehicle(@Mocked Transport transport, @Mocked Vertx vertx, @Mocked Context context, @Mocked JsonObject json) { HighwayServerVerticle highwayVehicle = new HighwayServerVerticle(); URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:9090"); new Expectations() { { transport.parseAddress(anyString); result = endpointObject; } }; Endpoint endpoint = new Endpoint(transport, "highway://127.0.0.1:9090"); new Expectations() { { context.config(); result = json; json.getValue(anyString); result = endpoint; } }; highwayVehicle.init(vertx, context); @SuppressWarnings("unchecked") Promise startPromise = Mockito.mock(Promise.class); highwayVehicle.startListen(startPromise); MockUtil.getInstance().mockHighwayConfig(); try { highwayVehicle.startListen(startPromise); Assertions.assertTrue(true); } catch (Exception e) { Assertions.fail(); } } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestHeaders.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHeaders { @Test public void test1() { Headers headers = new Headers(); Assertions.assertNull(headers.getFirst("h1")); Assertions.assertNull(headers.getHeader("h1")); Map> headerMap = new HashMap<>(); List h1Value = Arrays.asList("h1v1", "h1v2"); headerMap.put("h1", h1Value); headerMap.put("h2", null); headerMap.put("h3", Arrays.asList()); headers.setHeaderMap(headerMap); Assertions.assertEquals(headerMap, headers.getHeaderMap()); Assertions.assertEquals("h1v1", headers.getFirst("h1")); Assertions.assertNull(headers.getFirst("h2")); Assertions.assertNull(headers.getFirst("h3")); Assertions.assertEquals(h1Value, headers.getHeader("h1")); } @Test public void test2() { Headers headers = new Headers(); headers.addHeader("h1", "h1v1"); headers.addHeader("h1", "h1v2"); Assertions.assertEquals("h1v1", headers.getFirst("h1")); } @Test public void addHeader_list() { Headers headers = new Headers(); headers.addHeader("h", Arrays.asList("v1", "v2")); headers.addHeader("h", Arrays.asList("v3")); MatcherAssert.assertThat(headers.getHeader("h"), Matchers.contains("v1", "v2", "v3")); } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginRequest.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestLoginRequest { @Test public void test() { LoginRequest req = new LoginRequest(); req.setProtocol("p"); req.setZipName("z"); Assertions.assertEquals("p", req.getProtocol()); Assertions.assertEquals("z", req.getZipName()); } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestLoginResponse { @Test public void test() { LoginResponse resp = new LoginResponse(); resp.setProtocol("p"); resp.setZipName("z"); Assertions.assertEquals("p", resp.getProtocol()); Assertions.assertEquals("z", resp.getZipName()); } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestRequestHeader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestRequestHeader { private RequestHeader requestHeader = null; @Before public void setUp() throws Exception { requestHeader = new RequestHeader(); } @After public void tearDown() throws Exception { requestHeader = null; } @Test public void testContext() { Map context = null; requestHeader.setContext(context); Assertions.assertNull(requestHeader.getContext()); } @Test public void testDestMicroservice() { requestHeader.setDestMicroservice("test"); Assertions.assertEquals("test", requestHeader.getDestMicroservice()); } @Test public void testFlags() { requestHeader.setFlags(1); Assertions.assertEquals(1, requestHeader.getFlags()); } @Test public void testOperationName() { requestHeader.setOperationName("cse"); Assertions.assertEquals("cse", requestHeader.getOperationName()); } @Test public void testSchemaId() { requestHeader.setSchemaId("id"); Assertions.assertEquals("id", requestHeader.getSchemaId()); } } ================================================ FILE: transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestResponseHeader.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.highway.message; import java.util.HashMap; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; public class TestResponseHeader { private ResponseHeader responseHeader = null; Map context = new HashMap<>(); @Before public void setUp() throws Exception { responseHeader = new ResponseHeader(); } @After public void tearDown() throws Exception { responseHeader = null; } @Test public void testSetContext() { context.put("key1", "v1"); responseHeader.setContext(context); Assertions.assertNotNull(responseHeader.getContext()); Assertions.assertEquals("v1", responseHeader.getContext().get("key1")); } } ================================================ FILE: transports/transport-rest/pom.xml ================================================ 4.0.0 org.apache.servicecomb transports 3.4.0-SNAPSHOT transport-rest Java Chassis::Transports::Rest pom transport-rest-vertx transport-rest-servlet transport-rest-client ================================================ FILE: transports/transport-rest/transport-rest-client/pom.xml ================================================ 4.0.0 org.apache.servicecomb transport-rest 3.4.0-SNAPSHOT transport-rest-client Java Chassis::Transports::Rest::Client io.vertx vertx-rx-java3 org.apache.servicecomb foundation-vertx org.apache.servicecomb transport-common org.apache.servicecomb common-rest io.vertx vertx-codegen provided org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding test org.apache.servicecomb registry-local test org.apache.servicecomb swagger-generator-jaxrs test org.apache.commons commons-text test org.jmockit jmockit test ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/BoundaryFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; public interface BoundaryFactory { String BOUNDARY_PREFIX = "boundary-" + UUID.randomUUID() + "-"; AtomicLong BOUNDARY_INDEX = new AtomicLong(); BoundaryFactory DEFAULT = () -> BOUNDARY_PREFIX + BOUNDARY_INDEX.getAndIncrement(); String create(); } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/Http2TransportHttpClientOptionsSPI.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import io.vertx.core.http.HttpVersion; public class Http2TransportHttpClientOptionsSPI extends HttpTransportHttpClientOptionsSPI { public static final String CLIENT_NAME = "http2-transport-client"; @Override public String clientName() { return CLIENT_NAME; } @Override public int getOrder() { return super.getOrder() + 1; } @Override public boolean enabled() { return TransportClientConfig.isHttp2TransportClientEnabled(); } @Override public HttpVersion getHttpVersion() { return HttpVersion.HTTP_2; } @Override public String getWorkerPoolName() { return "pool-worker-transport-client-http2"; } @Override public boolean isUseAlpn() { return TransportClientConfig.getUseAlpn(); } @Override public int getHttp2MultiplexingLimit() { return TransportClientConfig.getHttp2MultiplexingLimit(); } @Override public int getHttp2MaxPoolSize() { return TransportClientConfig.getHttp2ConnectionMaxPoolSize(); } @Override public int getIdleTimeoutInSeconds() { return TransportClientConfig.getHttp2ConnectionIdleTimeoutInSeconds(); } @Override public int getKeepAliveTimeout() { return TransportClientConfig.getHttp2ConnectionKeepAliveTimeoutInSeconds(); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import org.apache.servicecomb.core.Invocation; import io.vertx.core.Future; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.RequestOptions; /** * some service has special domain name rule, eg: k8s
* assume k8s domain name is https://k8s.com:1234, and clusterId is my-id
* then must send request to https://my-id.k8s.com:1234
*
* this interface allowed to modify host by invocation argument, eg:
*
 * {@code
 *  Future create(Invocation invocation, HttpClient httpClient, HttpMethod method, RequestOptions options) {
 *    if ("k8s".equals(invocation.getMicroserviceName())) {
 *      options.setHost(invocation.getSwaggerArgument("clusterId") + "." + options.getHost());
 *    }
 *
 *    return httpClient.request(options);
 *  }
 * }
 * 
*/ public interface HttpClientRequestFactory { HttpClientRequestFactory DEFAULT = (invocation, httpClient, options) -> httpClient.request(options); Future create(Invocation invocation, HttpClient httpClient, RequestOptions options) throws Throwable; } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpVersion; import io.vertx.core.http.PoolOptions; public class HttpTransportHttpClientOptionsSPI implements HttpClientOptionsSPI { public static final String CLIENT_NAME = "http-transport-client"; public static final String CLIENT_TAG = "rest.consumer"; @Override public String clientName() { return CLIENT_NAME; } @Override public int getOrder() { return 100; } @Override public boolean enabled() { return TransportClientConfig.isHttpTransportClientEnabled(); } @Override public String getConfigTag() { return CLIENT_TAG; } @Override public int getEventLoopPoolSize() { // not reading this, using shared transport vert.x return -1; } @Override public boolean useSharedVertx() { return true; } @Override public int getInstanceCount() { return TransportClientConfig.getThreadCount(); } @Override public boolean isWorker() { return false; } @Override public String getWorkerPoolName() { return "pool-worker-transport-client-http"; } @Override public int getWorkerPoolSize() { return VertxOptions.DEFAULT_WORKER_POOL_SIZE; } @Override public HttpVersion getHttpVersion() { return HttpVersion.HTTP_1_1; } @Override public int getConnectTimeoutInMillis() { return TransportClientConfig.getConnectionTimeoutInMillis(); } @Override public int getIdleTimeoutInSeconds() { return TransportClientConfig.getConnectionIdleTimeoutInSeconds(); } @Override public boolean isTryUseCompression() { return TransportClientConfig.getConnectionCompression(); } @Override public int getMaxWaitQueueSize() { return TransportClientConfig.getMaxWaitQueueSize(); } @Override public int getMaxPoolSize() { return TransportClientConfig.getConnectionMaxPoolSize(); } @Override public boolean isKeepAlive() { return TransportClientConfig.getConnectionKeepAlive(); } @Override public int getMaxHeaderSize() { return TransportClientConfig.getMaxHeaderSize(); } @Override public int getKeepAliveTimeout() { return TransportClientConfig.getConnectionKeepAliveTimeoutInSeconds(); } @Override public boolean enableLogActivity() { return TransportClientConfig.enableLogActivity(); } @Override public int getHttp2MultiplexingLimit() { return HttpClientOptions.DEFAULT_HTTP2_MULTIPLEXING_LIMIT; } @Override public int getHttp2MaxPoolSize() { return PoolOptions.DEFAULT_HTTP2_MAX_POOL_SIZE; } @Override public boolean isUseAlpn() { return !HttpClientOptions.DEFAULT_ALPN_VERSIONS.isEmpty(); } @Override public boolean isProxyEnable() { // now transport proxy not implemented return false; } @Override public String getProxyHost() { return null; } @Override public int getProxyPort() { return 0; } @Override public String getProxyUsername() { return null; } @Override public String getProxyPassword() { return null; } @Override public boolean isSsl() { return true; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.beans.factory.annotation.Autowired; import io.vertx.core.http.HttpClientRequest; public class RestClientCodecFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String NAME = "rest-client-codec"; protected RestClientTransportContextFactory transportContextFactory; protected RestClientEncoder encoder; protected RestClientDecoder decoder; @Override public String getName() { return NAME; } @Override public boolean enabledForTransport(String transport) { return CoreConst.RESTFUL.equals(transport); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1990; } @Autowired public RestClientCodecFilter setTransportContextFactory(RestClientTransportContextFactory transportContextFactory) { this.transportContextFactory = transportContextFactory; return this; } @Autowired public RestClientCodecFilter setEncoder(RestClientEncoder encoder) { this.encoder = encoder; return this; } @Autowired public RestClientCodecFilter setDecoder(RestClientDecoder decoder) { this.decoder = decoder; return this; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { invocation.getInvocationStageTrace().startConsumerConnection(); CompletionStage createRequest = transportContextFactory.createHttpClientRequest(invocation).toCompletionStage() .whenComplete((c, e) -> invocation.getInvocationStageTrace().finishConsumerConnection()); return CompletableFuture.completedFuture(null) .thenCompose(v -> createRequest) .thenAccept(httpClientRequest -> prepareTransportContext(invocation, httpClientRequest)) .thenAccept(v -> invocation.onStartSendRequest()) .thenAccept(v -> encoder.encode(invocation)) .thenCompose(v -> nextNode.onFilter(invocation)) .thenApply(response -> decoder.decode(invocation, response)); } protected void prepareTransportContext(Invocation invocation, HttpClientRequest httpClientRequest) { copyExtraHttpHeaders(invocation, httpClientRequest); RestClientTransportContext transportContext = transportContextFactory.create(invocation, httpClientRequest); invocation.setTransportContext(transportContext); } @SuppressWarnings("unchecked") protected void copyExtraHttpHeaders(Invocation invocation, HttpClientRequest httpClientRequest) { Map httpHeaders = (Map) invocation.getHandlerContext() .get(RestConst.CONSUMER_HEADER); if (httpHeaders == null) { return; } httpHeaders.forEach((key, value) -> { if ("Content-Length".equalsIgnoreCase(key)) { return; } if (null != value) { httpClientRequest.putHeader(key, value); } }); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_DECODE_REST_FAIL_RESPONSE; import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_DECODE_REST_SUCCESS_RESPONSE; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import io.reactivex.rxjava3.core.Flowable; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import jakarta.ws.rs.core.HttpHeaders; public class RestClientDecoder { private static final Logger LOGGER = LoggerFactory.getLogger(RestClientDecoder.class); public Response decode(Invocation invocation, Response response) { invocation.getInvocationStageTrace().startConsumerDecodeResponse(); if (response.getResult() instanceof Buffer) { Object result = extractBody(invocation, response, response.getResult()); response.setResult(result); if (response.isFailed()) { throw Exceptions.create(response.getStatus(), response.getResult()); } } if (response.getResult() instanceof Flowable) { Flowable flowable = response.getResult(); Flowable encoded = flowable.map(buffer -> extractBody(invocation, response, buffer)); response.setResult(encoded); } invocation.getInvocationStageTrace().finishConsumerDecodeResponse(); return response; } protected Object extractBody(Invocation invocation, Response response, Buffer buffer) { ProduceProcessor produceProcessor = safeFindProduceProcessor(invocation, response); JavaType responseType = invocation.findResponseType(response.getStatusCode()); try { return produceProcessor.decodeResponse(buffer, responseType); } catch (Exception e) { throw createDecodeException(invocation, response, e); } } private ProduceProcessor safeFindProduceProcessor(Invocation invocation, Response response) { String contentType = extractContentType(response); ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE.createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), contentType, null); if (produceProcessor != null) { return produceProcessor; } RestClientTransportContext transportContext = invocation.getTransportContext(); HttpClientRequest httpClientRequest = transportContext.getHttpClientRequest(); LOGGER.warn( "operation={}, method={}, endpoint={}, uri={}, statusCode={}, reasonPhrase={}, response content-type={} is not supported in operation.", invocation.getMicroserviceQualifiedName(), httpClientRequest.getMethod(), invocation.getEndpoint().getEndpoint(), httpClientRequest.getURI(), response.getStatusCode(), response.getReasonPhrase(), response.getHeader(HttpHeaders.CONTENT_TYPE)); // This happens outside the runtime such as Servlet filter response. Here we give a default json parser to it // and keep user data not get lost. return ProduceProcessorManager.INSTANCE.findDefaultProcessor(); } protected String extractContentType(Response response) { String contentType = response.getHeader(HttpHeaders.CONTENT_TYPE); if (contentType == null) { return null; } int idx = contentType.indexOf(";"); return idx == -1 ? contentType : contentType.substring(0, idx); } protected InvocationException createDecodeException(Invocation invocation, Response response, Exception e) { RestClientTransportContext transportContext = invocation.getTransportContext(); HttpClientRequest httpClientRequest = transportContext.getHttpClientRequest(); LOGGER.warn("failed to decode response body, " + "operation={}, method={}, endpoint={}, uri={}, " + "statusCode={}, reasonPhrase={}, content-type={}.", invocation.getMicroserviceQualifiedName(), httpClientRequest.getMethod(), invocation.getEndpoint().getEndpoint(), httpClientRequest.getURI(), response.getStatusCode(), response.getReasonPhrase(), response.getHeader(HttpHeaders.CONTENT_TYPE)); if (response.isSucceed()) { return Exceptions.consumer(FAILED_TO_DECODE_REST_SUCCESS_RESPONSE, "failed to decode success response body.", e); } return Exceptions.consumer(FAILED_TO_DECODE_REST_FAIL_RESPONSE, "failed to decode fail response body.", e); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.MULTIPART_FORM_DATA; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_ENCODE_REST_CLIENT_REQUEST; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.servicecomb.common.rest.codec.RestCodec; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.query.QueryCodec; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationConfig; import org.apache.servicecomb.foundation.common.utils.StringBuilderUtils; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; /** * encode all send data except upload */ public class RestClientEncoder { private static final Logger LOGGER = LoggerFactory.getLogger(RestClientEncoder.class); public static final int FORM_BUFFER_SIZE = 1024; public void encode(Invocation invocation) { try { invocation.getInvocationStageTrace().startConsumerEncodeRequest(); EncoderSession encoderSession = new EncoderSession(invocation); encoderSession.doEncode(); invocation.getInvocationStageTrace().finishConsumerEncodeRequest(); } catch (Exception e) { throw new InvocationException(BAD_REQUEST, FAILED_TO_ENCODE_REST_CLIENT_REQUEST, e.getMessage(), e); } } public static class EncoderSession { protected final Invocation invocation; protected final RestClientTransportContext transportContext; protected final RestClientRequestParameters requestParameters; protected final HttpClientRequest httpClientRequest; public EncoderSession(Invocation invocation) { this.invocation = invocation; this.transportContext = invocation.getTransportContext(); this.requestParameters = this.transportContext.getRequestParameters(); this.httpClientRequest = this.transportContext.getHttpClientRequest(); } protected void doEncode() throws Exception { RestClientEncoder.LOGGER.debug("encode rest client request, operation={}, method={}, endpoint={}, uri={}.", invocation.getMicroserviceQualifiedName(), httpClientRequest.getMethod(), invocation.getEndpoint().getEndpoint(), httpClientRequest.getURI()); swaggerArgumentsToRequest(); writeCookies(requestParameters.getCookieMap()); writeScbHeaders(); writeForm(requestParameters.getFormMap()); } protected void swaggerArgumentsToRequest() throws Exception { RestCodec.argsToRest(invocation.getSwaggerArguments(), transportContext.getRestOperationMeta(), requestParameters); } protected void writeCookies(Map cookieMap) { if (CollectionUtils.isEmpty(cookieMap)) { return; } StringBuilder builder = new StringBuilder(); for (Entry entry : cookieMap.entrySet()) { builder.append(entry.getKey()) .append('=') .append(entry.getValue()) .append("; "); } StringBuilderUtils.deleteLast(builder, 2); httpClientRequest.putHeader(HttpHeaders.COOKIE, builder.toString()); } protected void writeScbHeaders() throws JsonProcessingException { OperationConfig operationConfig = invocation.getOperationMeta().getConfig(); if (operationConfig.isClientRequestHeaderFilterEnabled()) { return; } httpClientRequest.putHeader(CoreConst.TARGET_MICROSERVICE, invocation.getMicroserviceName()); httpClientRequest.putHeader(CoreConst.CSE_CONTEXT, RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(invocation.getContext())); } protected void writeForm(Map formMap) throws Exception { if (requestParameters.getUploads() == null) { writeUrlEncodedForm(formMap); return; } writeChunkedForm(formMap); } protected void writeUrlEncodedForm(Map formMap) throws Exception { if (formMap == null) { return; } httpClientRequest.putHeader(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); Buffer bodyBuffer = genUrlEncodedFormBuffer(formMap); requestParameters.setBodyBuffer(bodyBuffer); } protected Buffer genUrlEncodedFormBuffer(Map formMap) throws Exception { Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); for (Entry entry : formMap.entrySet()) { writeCharSequence(buffer, entry.getKey()); buffer.appendByte(((byte) '=')); String value = QueryCodec.convertToString(entry.getValue()); String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8); writeCharSequence(buffer, encodedValue); buffer.appendByte(((byte) '&')); } return buffer; } protected void writeChunkedForm(Map formMap) throws Exception { String boundary = transportContext.getOrCreateBoundary(); httpClientRequest.setChunked(true); httpClientRequest.putHeader(CONTENT_TYPE, MULTIPART_FORM_DATA + "; charset=UTF-8; boundary=" + boundary); if (formMap == null) { return; } Buffer bodyBuffer = genChunkedFormBuffer(formMap, boundary); requestParameters.setBodyBuffer(bodyBuffer); } protected Buffer genChunkedFormBuffer(Map formMap, String boundary) throws Exception { Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); for (Entry entry : formMap.entrySet()) { Object content = entry.getValue(); if (content instanceof List) { for (Object item : ((List) content)) { writeFormData(buffer, boundary, entry.getKey(), item); } } else { writeFormData(buffer, boundary, entry.getKey(), entry.getValue()); } } return buffer; } private void writeFormData(Buffer buffer, String boundary, String key, Object data) throws Exception { writeCharSequence(buffer, "\r\n--"); writeCharSequence(buffer, boundary); writeCharSequence(buffer, "\r\nContent-Disposition: form-data; name=\""); writeCharSequence(buffer, key); writeCharSequence(buffer, "\"\r\n\r\n"); String value = QueryCodec.convertToString(data); writeCharSequence(buffer, value); } } protected static void writeCharSequence(Buffer buffer, String value) { buffer.appendString(value, "UTF-8"); } public static Buffer genFileBoundaryBuffer(Part part, String name, String boundary) { Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); writeCharSequence(buffer, "\r\n--"); writeCharSequence(buffer, boundary); writeCharSequence(buffer, "\r\nContent-Disposition: form-data; name=\""); writeCharSequence(buffer, name); writeCharSequence(buffer, "\"; filename=\""); writeCharSequence(buffer, String.valueOf(part.getSubmittedFileName())); writeCharSequence(buffer, "\"\r\n"); writeCharSequence(buffer, "Content-Type: "); writeCharSequence(buffer, part.getContentType()); writeCharSequence(buffer, "\r\n"); writeCharSequence(buffer, "Content-Transfer-Encoding: binary\r\n"); writeCharSequence(buffer, "\r\n"); return buffer; } public static Buffer genBoundaryEndBuffer(String boundary) { Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); writeCharSequence(buffer, "\r\n--"); writeCharSequence(buffer, boundary); writeCharSequence(buffer, "--\r\n"); return buffer; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientExceptionCodes.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; public interface RestClientExceptionCodes { String FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT = "scb_rest_client.40000000"; String FAILED_TO_ENCODE_REST_CLIENT_REQUEST = "scb_rest_client.40000001"; String FAILED_TO_DECODE_REST_SUCCESS_RESPONSE = "scb_rest_client.40000002"; String FAILED_TO_DECODE_REST_FAIL_RESPONSE = "scb_rest_client.40000003"; } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParameters.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.Map; import jakarta.servlet.http.Part; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import com.google.common.collect.Multimap; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; public interface RestClientRequestParameters extends RestClientRequest { Map getCookieMap(); Map getFormMap(); Multimap getUploads(); @Override Buffer getBodyBuffer(); void setBodyBuffer(Buffer bodyBuffer); @Override default void write(Buffer bodyBuffer) { setBodyBuffer(bodyBuffer); } @Override default Future end() { throw new UnsupportedOperationException("should not invoke this method"); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.utils.PartUtils; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import jakarta.servlet.http.Part; public class RestClientRequestParametersImpl implements RestClientRequestParameters { protected final HttpClientRequest httpClientRequest; protected final MultiMap headers; protected Map cookieMap; protected Map formMap; protected Multimap uploads; protected Buffer bodyBuffer; public RestClientRequestParametersImpl(HttpClientRequest httpClientRequest) { this.httpClientRequest = httpClientRequest; this.headers = this.httpClientRequest.headers(); } @Override public Map getCookieMap() { return cookieMap; } @Override public void addCookie(String name, String value) { if (cookieMap == null) { cookieMap = new HashMap<>(); } cookieMap.put(name, value); } @Override public Map getFormMap() { return formMap; } @Override public void addForm(String name, Object value) { if (formMap == null) { formMap = new HashMap<>(); } if (value != null) { formMap.put(name, value); } } @Override public MultiMap getHeaders() { return headers; } @Override public void putHeader(String name, String value) { headers.add(name, value); } @Override public Buffer getBodyBuffer() { return bodyBuffer; } @Override public void setBodyBuffer(Buffer bodyBuffer) { this.bodyBuffer = bodyBuffer; } @Override public Multimap getUploads() { return uploads; } @SuppressWarnings("unchecked") @Override public void attach(String name, Object partOrList) { if (uploads == null) { uploads = ArrayListMultimap.create(); } if (partOrList == null) { return; } if (partOrList.getClass().isArray()) { for (Object part : (Object[]) partOrList) { uploads.put(name, PartUtils.getSinglePart(name, part)); } return; } if (partOrList instanceof Collection) { for (Object part : ((Collection) partOrList)) { uploads.put(name, PartUtils.getSinglePart(name, part)); } return; } uploads.put(name, PartUtils.getSinglePart(name, partOrList)); } @Override public HttpClientRequest getHttpClientRequest() { return this.httpClientRequest; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import static org.apache.servicecomb.transport.rest.client.RestClientEncoder.genBoundaryEndBuffer; import static org.apache.servicecomb.transport.rest.client.RestClientEncoder.genFileBoundaryBuffer; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.http.HttpStatus; import org.apache.servicecomb.foundation.vertx.executor.VertxContextExecutor; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Multimap; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.Promise; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.rxjava3.FlowableHelper; import jakarta.servlet.http.Part; public class RestClientSender { private static final Logger LOGGER = LoggerFactory.getLogger(RestClientSender.class); protected final Invocation invocation; protected final RestClientTransportContext transportContext; protected final RestClientRequestParameters requestParameters; protected final HttpClientRequest httpClientRequest; protected final CompletableFuture future = new CompletableFuture<>(); public RestClientSender(Invocation invocation) { this.invocation = invocation; this.transportContext = invocation.getTransportContext(); this.requestParameters = transportContext.getRequestParameters(); this.httpClientRequest = transportContext.getHttpClientRequest(); } public CompletableFuture send() { invocation.getInvocationStageTrace().startConsumerSendRequest(); httpClientRequest.response().compose(this::processResponse).onFailure(future::completeExceptionally); CompletableFuture actualFuture = future.whenComplete(this::afterSend); VertxContextExecutor.create(transportContext.getVertxContext()).execute(this::runInVertxContext); return actualFuture; } protected void runInVertxContext() { sendInVertxContext() .whenComplete((v, e) -> { if (e != null) { future.completeExceptionally(e); } invocation.getInvocationStageTrace().finishConsumerSendRequest(); invocation.getInvocationStageTrace().startWaitResponse(); }); } protected CompletableFuture sendInVertxContext() { httpClientRequest.idleTimeout(invocation.getOperationMeta().getConfig().getMsRequestTimeout()); Multimap uploads = requestParameters.getUploads(); if (uploads == null) { if (requestParameters.getBodyBuffer() != null) { return CompletableFuture.completedFuture(null).thenCompose( v -> httpClientRequest.end(requestParameters.getBodyBuffer()).toCompletionStage()); } else { return CompletableFuture.completedFuture(null).thenCompose( v -> httpClientRequest.end().toCompletionStage()); } } if (requestParameters.getBodyBuffer() != null) { httpClientRequest.write(requestParameters.getBodyBuffer()); } return sendFiles(); } protected CompletableFuture sendFiles() { CompletableFuture sendFileFuture = CompletableFuture.completedFuture(null); String boundary = transportContext.getOrCreateBoundary(); for (Entry entry : requestParameters.getUploads().entries()) { // do not use part.getName() to get parameter name // because pojo consumer not easy to set name to part String name = entry.getKey(); sendFileFuture = sendFileFuture.thenCompose(v -> sendFile(entry.getValue(), name, boundary)); } return sendFileFuture.thenCompose(v -> httpClientRequest.end(genBoundaryEndBuffer(boundary)).toCompletionStage()); } private CompletableFuture sendFile(Part part, String name, String boundary) { Buffer fileHeader = genFileBoundaryBuffer(part, name, boundary); httpClientRequest.write(fileHeader); return new PumpFromPart(transportContext.getVertxContext(), part) .toWriteStream(httpClientRequest, future::completeExceptionally) .whenComplete((v, e) -> { if (e != null) { LOGGER.debug("Failed to send file [{}:{}].", name, part.getSubmittedFileName(), e); return; } LOGGER.debug("finish send file [{}:{}].", name, part.getSubmittedFileName()); }); } protected Future processResponse(HttpClientResponse httpClientResponse) { Promise result = Promise.promise(); transportContext.setHttpClientResponse(httpClientResponse); if (!HttpStatus.isSuccess(httpClientResponse.statusCode())) { httpClientResponse.body().compose(buffer -> { future.complete(createResponse(httpClientResponse, buffer)); result.complete(); return Future.succeededFuture(); }); return result.future(); } if (transportContext.isDownloadFile()) { ReadStreamPart streamPart = new ReadStreamPart(transportContext.getVertxContext(), httpClientResponse); future.complete(createResponse(httpClientResponse, streamPart)); result.complete(); return result.future(); } if (transportContext.isServerSendEvents()) { future.complete(createResponse(httpClientResponse, FlowableHelper.toFlowable(httpClientResponse))); result.complete(); return result.future(); } httpClientResponse.body().compose(buffer -> { future.complete(createResponse(httpClientResponse, buffer)); result.complete(); return Future.succeededFuture(); }); return result.future(); } protected Response createResponse(HttpClientResponse httpClientResponse, Object result) { // http2's :status header will cause edge forward failed MultiMap headers = httpClientResponse.headers(); headers.remove(":status"); HttpStatus httpStatus = new HttpStatus(httpClientResponse.statusCode(), httpClientResponse.statusMessage()); return Response .status(httpStatus) .setHeaders(headers) .entity(result); } protected void afterSend(Response response, Throwable throwable) { invocation.getInvocationStageTrace().finishWaitResponse(); if (throwable != null) { LOGGER.error("rest client send or receive failed, operation={}, method={}, endpoint={}, uri={}.", invocation.getMicroserviceQualifiedName(), httpClientRequest.getMethod(), invocation.getEndpoint().getEndpoint(), httpClientRequest.getURI()); } } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.swagger.invocation.Response; public class RestClientSenderFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String NAME = "rest-client-sender"; @Override public String getName() { return NAME; } @Override public boolean enabledForTransport(String transport) { return CoreConst.RESTFUL.equals(transport); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { CompletableFuture future = new RestClientSender(invocation) .send(); return invocation.optimizeSyncConsumerThread(future); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.Optional; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; import io.vertx.core.Context; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpConnection; public class RestClientTransportContext implements VertxTransportContext { protected final RestOperationMeta restOperationMeta; protected final Context vertxContext; protected final HttpClientRequest httpClientRequest; protected final RestClientRequestParameters requestParameters; protected final BoundaryFactory boundaryFactory; protected String boundary; protected HttpClientResponse httpClientResponse; public RestClientTransportContext(RestOperationMeta restOperationMeta, Context vertxContext, HttpClientRequest httpClientRequest, BoundaryFactory boundaryFactory) { this.restOperationMeta = restOperationMeta; this.vertxContext = vertxContext; this.httpClientRequest = httpClientRequest; this.boundaryFactory = boundaryFactory; this.requestParameters = new RestClientRequestParametersImpl(httpClientRequest); } public RestOperationMeta getRestOperationMeta() { return restOperationMeta; } public boolean isDownloadFile() { return restOperationMeta.isDownloadFile(); } public boolean isServerSendEvents() { return restOperationMeta.isServerSendEvents(); } @Override public Context getVertxContext() { return vertxContext; } public HttpClientRequest getHttpClientRequest() { return httpClientRequest; } public RestClientRequestParameters getRequestParameters() { return requestParameters; } public String getOrCreateBoundary() { if (boundary == null) { boundary = boundaryFactory.create(); } return boundary; } public HttpClientResponse getHttpClientResponse() { return httpClientResponse; } public RestClientTransportContext setHttpClientResponse(HttpClientResponse httpClientResponse) { this.httpClientResponse = httpClientResponse; return this; } public String getLocalAddress() { return Optional.ofNullable(httpClientRequest.connection()) .map(HttpConnection::localAddress) .map(Object::toString) .orElse("not connected"); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.definition.RestMetaUtils; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import io.vertx.core.Future; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.RequestOptions; public class RestClientTransportContextFactory { private BoundaryFactory boundaryFactory = BoundaryFactory.DEFAULT; private HttpClientRequestFactory httpClientRequestFactory = HttpClientRequestFactory.DEFAULT; @Autowired(required = false) public RestClientTransportContextFactory setBoundaryFactory(BoundaryFactory boundaryFactory) { this.boundaryFactory = boundaryFactory; return this; } @Autowired(required = false) public RestClientTransportContextFactory setHttpClientRequestFactory(HttpClientRequestFactory factory) { this.httpClientRequestFactory = factory; return this; } public RestClientTransportContext create(Invocation invocation, HttpClientRequest httpClientRequest) { try { return doCreate(invocation, httpClientRequest); } catch (Throwable e) { throw new InvocationException(BAD_REQUEST, FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT, e.getMessage(), e); } } protected RestClientTransportContext doCreate(Invocation invocation, HttpClientRequest httpClientRequest) throws Throwable { RestOperationMeta restOperationMeta = RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()); HttpClientWithContext httpClientWithContext = findHttpClientPool(invocation); RestClientTransportContext context = new RestClientTransportContext(restOperationMeta, httpClientWithContext.context(), httpClientRequest, boundaryFactory); invocation.getHandlerContext().put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, context.getRequestParameters()); return context; } protected HttpClientWithContext findHttpClientPool(Invocation invocation) { URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); if (endpoint.isHttp2Enabled()) { return HttpClients.getClient(Http2TransportHttpClientOptionsSPI.CLIENT_NAME, invocation.isSync()); } return HttpClients.getClient(HttpTransportHttpClientOptionsSPI.CLIENT_NAME, invocation.isSync()); } protected Future createHttpClientRequest(Invocation invocation) { try { RestOperationMeta restOperationMeta = RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()); HttpClientWithContext httpClientWithContext = findHttpClientPool(invocation); URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); HttpMethod method = HttpMethod.valueOf(restOperationMeta.getHttpMethod()); RequestOptions requestOptions = new RequestOptions() .setHost(endpoint.getHostOrIp()) .setPort(endpoint.getPort()) .setSsl(endpoint.isSslEnabled()) .setMethod(method) .setURI(createRequestPath(invocation, restOperationMeta)); return httpClientRequestFactory.create(invocation, httpClientWithContext.getHttpClient(), requestOptions); } catch (Throwable e) { throw new InvocationException(BAD_REQUEST, FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT, e.getMessage(), e); } } protected String createRequestPath(Invocation invocation, RestOperationMeta restOperationMeta) throws Exception { String path = invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH); if (path == null) { path = restOperationMeta.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); } URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); String urlPrefix = endpoint.getFirst(DefinitionConst.URL_PREFIX); if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { return path; } return urlPrefix + path; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.transport.common.TransportConfigUtils; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.PoolOptions; public final class TransportClientConfig { private static final int DEFAULT_IDLE_TIME_OUT = 150; private static final int DEFAULT_KEEP_ALIVE_TIME_OUT = 60; private TransportClientConfig() { } public static int getThreadCount() { return TransportConfigUtils.readVerticleCount( "servicecomb.rest.client.verticle-count", "servicecomb.rest.client.thread-count"); } public static int getHttp2ConnectionMaxPoolSize() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.maxPoolSize", PoolOptions.DEFAULT_HTTP2_MAX_POOL_SIZE); } public static int getHttp2MultiplexingLimit() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.multiplexingLimit", HttpClientOptions.DEFAULT_HTTP2_MULTIPLEXING_LIMIT); } public static boolean getUseAlpn() { return LegacyPropertyFactory.getBooleanProperty("servicecomb.rest.client.http2.useAlpnEnabled", true); } public static boolean isHttp2TransportClientEnabled() { return LegacyPropertyFactory.getBooleanProperty("servicecomb.rest.client.http2.enabled", true); } public static int getConnectionMaxPoolSize() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.connection.maxPoolSize", PoolOptions.DEFAULT_MAX_POOL_SIZE); } public static int getHttp2ConnectionIdleTimeoutInSeconds() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.connection.idleTimeoutInSeconds", DEFAULT_IDLE_TIME_OUT); } public static int getConnectionIdleTimeoutInSeconds() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.connection.idleTimeoutInSeconds", DEFAULT_IDLE_TIME_OUT); } public static boolean getConnectionKeepAlive() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.client.connection.keepAlive", HttpClientOptions.DEFAULT_KEEP_ALIVE); } public static int getConnectionKeepAliveTimeoutInSeconds() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.connection.keepAliveTimeoutInSeconds", DEFAULT_KEEP_ALIVE_TIME_OUT); } public static boolean enableLogActivity() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.client.enableLogActivity", false); } public static int getHttp2ConnectionKeepAliveTimeoutInSeconds() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds", DEFAULT_KEEP_ALIVE_TIME_OUT); } public static boolean getConnectionCompression() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.client.connection.compression", HttpClientOptions.DEFAULT_DECOMPRESSION_SUPPORTED); } public static int getMaxHeaderSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.maxHeaderSize", HttpClientOptions.DEFAULT_MAX_HEADER_SIZE); } public static int getMaxWaitQueueSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.maxWaitQueueSize", PoolOptions.DEFAULT_MAX_WAIT_QUEUE_SIZE); } public static boolean isHttpTransportClientEnabled() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.client.enabled", true); } public static int getConnectionTimeoutInMillis() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.connection.timeoutInMillis", 60000); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TransportRestClientConfiguration { @Bean public RestClientSenderFilter restClientSenderFilter() { return new RestClientSenderFilter(); } @Bean public RestClientCodecFilter restClientCodecFilter() { return new RestClientCodecFilter(); } @Bean public WebSocketClientCodecFilter webSocketClientCodecFilter() { return new WebSocketClientCodecFilter(); } @Bean public RestClientDecoder restClientDecoder() { return new RestClientDecoder(); } @Bean public RestClientTransportContextFactory restClientTransportContextFactory() { return new RestClientTransportContextFactory(); } @Bean public RestClientEncoder restClientEncoder() { return new RestClientEncoder(); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.WebSocketTransportContext; import org.apache.servicecomb.common.rest.definition.RestMetaUtils; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.http.ServerWebSocket; import io.vertx.core.http.WebSocket; import io.vertx.core.http.WebSocketClient; public class WebSocketClientCodecFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketClientCodecFilter.class); public static final String NAME = "websocket-client"; @Override public String getName() { return NAME; } @Override public boolean enabledForTransport(String transport) { return CoreConst.WEBSOCKET.equals(transport); } @Override public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { invocation.getInvocationStageTrace().startConsumerConnection(); CompletableFuture createWebSocket = new CompletableFuture<>(); URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); HttpClientOptionsSPI optionsSPI; if (endpoint.isHttp2Enabled()) { optionsSPI = SPIServiceUtils.getTargetService(HttpClientOptionsSPI.class, HttpTransportHttpClientOptionsSPI.class); } else { optionsSPI = SPIServiceUtils.getTargetService(HttpClientOptionsSPI.class, Http2TransportHttpClientOptionsSPI.class); } WebSocketClient webSocketClient = HttpClients.createWebSocketClient(optionsSPI, endpoint.isSslEnabled()); try { webSocketClient.connect(endpoint.getPort(), endpoint.getHostOrIp(), createRequestPath(invocation, RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()))) .onComplete(asyncResult -> { invocation.getInvocationStageTrace().finishConsumerConnection(); if (asyncResult.failed()) { createWebSocket.completeExceptionally(asyncResult.cause()); return; } if (invocation.isEdge()) { WebSocketTransportContext parentContext = invocation.getTransportContext(); ServerWebSocket serverWebSocket = parentContext.getServerWebSocket(); WebSocket clientWebSocket = asyncResult.result(); serverWebSocket.closeHandler(v -> { if (!clientWebSocket.isClosed()) { clientWebSocket.close(); } }); serverWebSocket.textMessageHandler(clientWebSocket::writeTextMessage); serverWebSocket.binaryMessageHandler(clientWebSocket::writeBinaryMessage); serverWebSocket.exceptionHandler(e -> { LOGGER.warn("consumer exception.", e); if (!serverWebSocket.isClosed()) { serverWebSocket.close(); } }); clientWebSocket.closeHandler(v -> { if (!serverWebSocket.isClosed()) { serverWebSocket.close(); } }); clientWebSocket.textMessageHandler(serverWebSocket::writeTextMessage); clientWebSocket.binaryMessageHandler(serverWebSocket::writeBinaryMessage); clientWebSocket.exceptionHandler(e -> { LOGGER.warn("producer exception.", e); if (!clientWebSocket.isClosed()) { clientWebSocket.close(); } }); } invocation.setTransportContext(new WebSocketClientTransportContext( asyncResult.result())); createWebSocket.complete(Response.createSuccess(asyncResult.result())); }); } catch (Exception e) { createWebSocket.completeExceptionally(e); } return createWebSocket; } protected String createRequestPath(Invocation invocation, RestOperationMeta restOperationMeta) throws Exception { String path = invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH); if (path == null) { path = restOperationMeta.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); } URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); String urlPrefix = endpoint.getFirst(DefinitionConst.URL_PREFIX); if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { return path; } return urlPrefix + path; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import org.apache.servicecomb.swagger.invocation.context.TransportContext; import io.vertx.core.http.WebSocket; public class WebSocketClientTransportContext implements TransportContext { protected final WebSocket webSocketClient; public WebSocketClientTransportContext(WebSocket webSocketClient) { this.webSocketClient = webSocketClient; } public WebSocket getWebSocketClient() { return webSocketClient; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.transport.rest.client.HttpTransportHttpClientOptionsSPI org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI ================================================ FILE: transports/transport-rest/transport-rest-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ ## --------------------------------------------------------------------------- ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. ## The ASF licenses this file to You under the Apache License, Version 2.0 ## (the "License"); you may not use this file except in compliance with ## the License. You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- org.apache.servicecomb.transport.rest.client.TransportRestClientConfiguration ================================================ FILE: transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/FakeRestTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; class FakeRestTransport extends AbstractTransport { @Override public String getName() { return null; } @Override public boolean init() { return false; } @Override public Object parseAddress(String address) { return new URIEndpointObject(address); } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/RestFeatureController.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.client; import java.io.File; import java.util.List; import jakarta.ws.rs.CookieParam; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; @Path("/") public class RestFeatureController { public static final String SCHEMA_ID = "rest-feature"; @GET @Path("/query") public String query(@QueryParam("query") String query) { return query; } @GET @Path("/header") public String header(@HeaderParam("header") String header) { return header; } @GET @Path("/cookie") public String cookie(@CookieParam("cookie1") String cookie1, @CookieParam("cookie2") String cookie2) { return cookie1 + ":" + cookie2; } @POST @Path("/form") public String form(@FormParam("form1") String form1, @FormParam("form2") String form2) { return form1 + ":" + form2; } @POST @Path("/formWithUpload") public String formWithUpload(@FormParam("form1") String form1, @FormParam("form2") File form2) { return form1 + ":" + form2.getName(); } @POST @Path("/formWithUploadList") public String formWithUploadList(@FormParam("files") List files) { return files.toString(); } @POST @Path("/body") public String body(String body) { return body; } } ================================================ FILE: transports/transport-rest/transport-rest-client/src/test/resources/log4j2.xml ================================================ ================================================ FILE: transports/transport-rest/transport-rest-servlet/pom.xml ================================================ 4.0.0 org.apache.servicecomb transport-rest 3.4.0-SNAPSHOT transport-rest-servlet Java Chassis::Transports::Rest::Servlet jakarta.servlet jakarta.servlet-api org.springframework spring-webmvc org.apache.servicecomb transport-rest-client org.apache.servicecomb registry-service-center test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding org.jmockit jmockit test ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestAsyncListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.AsyncEvent; import jakarta.servlet.AsyncListener; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; public class RestAsyncListener implements AsyncListener { private static final Logger LOGGER = LoggerFactory.getLogger(RestAsyncListener.class); private static String TIMEOUT_MESSAGE; static { try { TIMEOUT_MESSAGE = JsonUtils.writeValueAsString(new CommonExceptionData("Timeout when processing the request.")); } catch (JsonProcessingException e) { LOGGER.error("Failed to init timeout message.", e); } } @Override public void onComplete(AsyncEvent event) throws IOException { } @Override public void onTimeout(AsyncEvent event) throws IOException { // in this time, maybe: // 1.invocation in executor's queue // 2.already executing in executor // 3.already send response // to avoid concurrent, must lock request ServletRequest request = event.getSuppliedRequest(); HttpServletRequestEx requestEx = (HttpServletRequestEx) request.getAttribute(RestConst.REST_REQUEST); LOGGER.error("Rest request timeout, method {}, path {}.", requestEx.getMethod(), requestEx.getRequestURI()); // Waiting till executing in executor done. This operation may block container pool and make timeout requests in executor's // queue getting executed, and will cause bad performance. So default timeout is setting to -1 to disable timeout. synchronized (requestEx) { ServletResponse response = event.getAsyncContext().getResponse(); if (!response.isCommitted()) { // invocation in executor's queue response.setContentType(MediaType.APPLICATION_JSON); // we don't know if developers declared one statusCode in contract // so we use cse inner statusCode here ((HttpServletResponse) response).setStatus(Status.INTERNAL_SERVER_ERROR.getStatusCode()); PrintWriter out = response.getWriter(); out.write(TIMEOUT_MESSAGE); response.flushBuffer(); } request.removeAttribute(RestConst.REST_REQUEST); } LOGGER.error("Rest request timeout committed, method {}, path {}.", requestEx.getMethod(), requestEx.getRequestURI()); } @Override public void onError(AsyncEvent event) throws IOException { // 未使用 } @Override public void onStartAsync(AsyncEvent event) throws IOException { // 未使用 } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServlet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Rest Servlet Server, load by web container */ public class RestServlet extends HttpServlet { private static final long serialVersionUID = 5797523329773923112L; private static final Logger LOGGER = LoggerFactory.getLogger(RestServlet.class); private final ServletRestDispatcher servletRestServer = new ServletRestDispatcher(); @Override public void init() throws ServletException { super.init(); LOGGER.info("Rest Servlet inited"); } @Override public void service(final HttpServletRequest request, final HttpServletResponse response) { servletRestServer.service(request, response); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration.Dynamic; public class RestServletInjector { private static final Logger LOGGER = LoggerFactory.getLogger(RestServletInjector.class); public static final String SERVLET_NAME = "ServicecombRestServlet"; public static Dynamic defaultInject(ServletContext servletContext, Environment environment) { RestServletInjector injector = new RestServletInjector(); String urlPattern = ServletConfig.getServletUrlPattern(environment); return injector.inject(servletContext, urlPattern, environment); } public Dynamic inject(ServletContext servletContext, String urlPattern, Environment environment) { String[] urlPatterns = splitUrlPattern(urlPattern); if (urlPatterns.length == 0) { LOGGER.warn("urlPattern is empty, ignore register {}.", SERVLET_NAME); return null; } String listenAddress = ServletConfig.getLocalServerAddress(environment); if (!ServletUtils.canPublishEndpoint(listenAddress)) { LOGGER.warn("ignore register {}.", SERVLET_NAME); return null; } // dynamic deploy a servlet to handle serviceComb RESTful request Dynamic dynamic = servletContext.addServlet(SERVLET_NAME, RestServlet.class); dynamic.setAsyncSupported(true); dynamic.addMapping(urlPatterns); dynamic.setLoadOnStartup(0); LOGGER.info("RESTful servlet url pattern: {}.", Arrays.toString(urlPatterns)); return dynamic; } private String[] splitUrlPattern(String urlPattern) { if (StringUtils.isEmpty(urlPattern)) { return new String[] {}; } return ServletUtils.filterUrlPatterns(urlPattern); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestProducerInvocationCreator; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; public class RestServletProducerInvocationCreator extends RestProducerInvocationCreator { public RestServletProducerInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { super(microserviceMeta, endpoint, requestEx, responseEx); } @Override protected void initTransportContext(Invocation invocation) { HttpTransportContext transportContext = new HttpTransportContext(requestEx, responseEx); invocation.setTransportContext(transportContext); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import org.springframework.core.env.Environment; public final class ServletConfig { public static final long DEFAULT_ASYN_SERVLET_TIMEOUT = -1; public static final String KEY_SERVLET_URL_PATTERN = "servicecomb.rest.servlet.urlPattern"; public static final String SERVICECOMB_REST_ADDRESS = "servicecomb.rest.address"; public static final String KEY_SERVICECOMB_ASYC_SERVLET_TIMEOUT = "servicecomb.rest.server.timeout"; public static final String DEFAULT_URL_PATTERN = "/*"; private ServletConfig() { } public static long getAsyncServletTimeout(Environment environment) { return environment.getProperty(KEY_SERVICECOMB_ASYC_SERVLET_TIMEOUT, long.class, DEFAULT_ASYN_SERVLET_TIMEOUT); } public static String getLocalServerAddress(Environment environment) { return environment.getProperty(SERVICECOMB_REST_ADDRESS); } public static String getServletUrlPattern(Environment environment) { return environment.getProperty(KEY_SERVLET_URL_PATTERN, DEFAULT_URL_PATTERN); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.StandardHttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.StandardHttpServletResponseEx; import jakarta.servlet.AsyncContext; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; public class ServletRestDispatcher { private final RestAsyncListener restAsyncListener = new RestAsyncListener(); private Transport transport; private MicroserviceMeta microserviceMeta; public void service(HttpServletRequest request, HttpServletResponse response) { if (transport == null) { transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL); microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); } // 异步场景 AsyncContext asyncCtx = request.startAsync(); asyncCtx.addListener(restAsyncListener); asyncCtx.setTimeout(ServletConfig.getAsyncServletTimeout(SCBEngine.getInstance().getEnvironment())); HttpServletRequestEx requestEx = new StandardHttpServletRequestEx(request); HttpServletResponseEx responseEx = new StandardHttpServletResponseEx(response); ((StandardHttpServletRequestEx) requestEx).setCacheRequest(true); InvocationCreator creator = new RestServletProducerInvocationCreator(microserviceMeta, transport.getEndpoint(), requestEx, responseEx); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServletRestTransport extends AbstractTransport { private static final Logger LOGGER = LoggerFactory.getLogger(ServletRestTransport.class); @Override public String getName() { return CoreConst.RESTFUL; } @Override public boolean canInit() { String listenAddress = ServletConfig.getLocalServerAddress(environment); if (listenAddress == null) { // not publish, but can init and be RESTful client return true; } if (!ServletUtils.canPublishEndpoint(listenAddress)) { LOGGER.info("ignore transport {}.", this.getClass().getName()); return false; } return true; } @Override public boolean init() { String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); Map queryMap = new HashMap<>(); if (!StringUtils.isEmpty(urlPrefix)) { queryMap.put(DefinitionConst.URL_PREFIX, urlPrefix); } String listenAddress = ServletConfig.getLocalServerAddress(environment); setListenAddressWithoutSchema(listenAddress, queryMap); return true; } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.UploadConfig; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.common.net.IpPort; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; import jakarta.servlet.ServletRegistration.Dynamic; public class ServletUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ServletUtils.class); public static boolean canPublishEndpoint(String listenAddress) { if (StringUtils.isEmpty(listenAddress)) { LOGGER.info("listenAddress is null, can not publish."); return false; } IpPort ipPort = NetUtils.parseIpPortFromURI("http://" + listenAddress); if (ipPort == null) { LOGGER.info("invalid listenAddress {}, can not publish, format should be ip:port.", listenAddress); return false; } if (NetUtils.canTcpListen(ipPort.getSocketAddress().getAddress(), ipPort.getPort())) { LOGGER.info("{} is not listened, can not publish.", ipPort.getSocketAddress()); return false; } return true; } // we only support path prefix rule // other invalid urlPattern will be check by web container, we do not handle that static void checkUrlPattern(String urlPattern) { if (!urlPattern.startsWith("/")) { throw new ServiceCombException("only support rule like /* or /path/* or /path1/path2/* and so on."); } int idx = urlPattern.indexOf("/*"); if (idx < 0 || idx != urlPattern.length() - 2) { throw new ServiceCombException("only support rule like /* or /path/* or /path1/path2/* and so on."); } } static String[] filterUrlPatterns(String... urlPatterns) { return filterUrlPatterns(Arrays.asList(urlPatterns)); } static String[] filterUrlPatterns(Collection urlPatterns) { return urlPatterns.stream() .filter(pattern -> !pattern.trim().isEmpty()) .filter(pattern -> { checkUrlPattern(pattern.trim()); return true; }) .toArray(String[]::new); } static String[] collectUrlPatterns(ServletContext servletContext, Class servletCls) { List servlets = findServletRegistrations(servletContext, servletCls); if (servlets.isEmpty()) { return new String[] {}; } ServletRegistration servletRegistration = servlets.get(0); Collection mappings = servletRegistration.getMappings(); if (servlets.size() > 1) { LOGGER.info("Found {} {} registered, select the first one, mappings={}.", servlets.size(), servletCls.getName(), mappings); } return filterUrlPatterns(mappings); } static List findServletRegistrations(ServletContext servletContext, Class servletCls) { if (servletContext == null) { return null; } return servletContext.getServletRegistrations() .values() .stream() .filter(predicate -> predicate.getClassName().equals(servletCls.getName())) .collect(Collectors.toList()); } static String collectUrlPrefix(ServletContext servletContext, Class servletCls) { String[] urlPatterns = collectUrlPatterns(servletContext, servletCls); if (urlPatterns.length == 0) { return null; } // even have multiple urlPattern, we only choose one to set as urlPrefix // only make sure sdk can invoke String urlPattern = urlPatterns[0]; return servletContext.getContextPath() + urlPattern.substring(0, urlPattern.length() - 2); } public static void saveUrlPrefix(ServletContext servletContext) { String urlPrefix = collectUrlPrefix(servletContext, RestServlet.class); if (urlPrefix == null) { LOGGER.info("RestServlet not found, will not save UrlPrefix."); return; } ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, urlPrefix); LOGGER.info("UrlPrefix of this instance is \"{}\".", urlPrefix); } static File createUploadDir(ServletContext servletContext, String location) { // If relative, it is relative to TEMPDIR File dir = new File(location); if (!dir.isAbsolute()) { dir = new File((File) servletContext.getAttribute(ServletContext.TEMPDIR), location).getAbsoluteFile(); } if (!dir.exists()) { dir.mkdirs(); } return dir; } static void setServletParameters(ServletContext servletContext, Environment environment) { UploadConfig uploadConfig = new UploadConfig(environment); MultipartConfigElement multipartConfig = uploadConfig.toMultipartConfigElement(); File dir = createUploadDir(servletContext, multipartConfig.getLocation()); LOGGER.info("set uploads directory to \"{}\".", dir.getAbsolutePath()); List servlets = findServletRegistrations(servletContext, RestServlet.class); if (servlets != null) { for (ServletRegistration servletRegistration : servlets) { if (!Dynamic.class.isInstance(servletRegistration)) { continue; } Dynamic dynamic = (Dynamic) servletRegistration; dynamic.setMultipartConfig(multipartConfig); } } } public static void init(ServletContext servletContext, Environment environment) { RestServletInjector.defaultInject(servletContext, environment); ServletUtils.saveUrlPrefix(servletContext); setServletParameters(servletContext, environment); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.transport.rest.servlet.ServletRestTransport ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestAsyncListener.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncEvent; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.StandardHttpServletRequestEx; import org.junit.Before; import org.junit.Test; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; import org.junit.jupiter.api.Assertions; public class TestRestAsyncListener { HttpServletRequest request = new AbstractHttpServletRequest() { public String getMethod() { return "GET"; } public String getRequestURI() { return "path"; } }; HttpServletRequestEx requestEx = new StandardHttpServletRequestEx(request); boolean committed; boolean flushed; int statusCode; String contentType; Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); @Mocked HttpServletResponse response; @Mocked AsyncContext context; AsyncEvent event; RestAsyncListener listener = new RestAsyncListener(); @Before public void setup() { event = new AsyncEvent(context, requestEx, response); requestEx.setAttribute(RestConst.REST_REQUEST, requestEx); new MockUp(response) { @Mock void setContentType(String type) { contentType = type; } @Mock void setStatus(int sc) { statusCode = sc; } @Mock boolean isCommitted() { return committed; } @Mock PrintWriter getWriter() throws IOException { return printWriter; } @Mock void flushBuffer() throws IOException { flushed = true; } }; } @Test public void onTimeoutCommitted() throws IOException { committed = true; listener.onTimeout(event); Assertions.assertNull(request.getAttribute(RestConst.REST_REQUEST)); Assertions.assertFalse(flushed); } @Test public void onTimeoutNotCommitted() throws IOException { committed = false; listener.onTimeout(event); Assertions.assertNull(request.getAttribute(RestConst.REST_REQUEST)); Assertions.assertEquals(MediaType.APPLICATION_JSON, contentType); Assertions.assertEquals(500, statusCode); Assertions.assertTrue(flushed); Assertions.assertEquals("{\"message\":\"Timeout when processing the request.\"}", writer.toString()); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.impl.SysProps; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import mockit.Deencapsulation; import mockit.Mock; import mockit.MockUp; public class TestRestServlet { private RestServlet restservlet = null; Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { Mockito.when(environment.getProperty(PUBLISH_ADDRESS, String.class, "")) .thenReturn(""); Mockito.when(environment.getProperty("servicecomb.rest.publishPort", int.class, 0)) .thenReturn(0); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); restservlet = new RestServlet(); SCBBootstrap.createSCBEngineForTest(environment); } @After public void tearDown() { restservlet = null; SCBEngine.getInstance().destroy(); } @Test public void testInit() throws ServletException { restservlet.init(); Assertions.assertTrue(true); } // useless, but for coverage @Test public void testService() { Holder holder = new Holder<>(); ServletRestDispatcher servletRestServer = new MockUp() { @Mock void service(HttpServletRequest request, HttpServletResponse response) { holder.value = true; } }.getMockInstance(); Deencapsulation.setField(restservlet, "servletRestServer", servletRestServer); restservlet.service(null, null); Assertions.assertTrue(holder.value); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServletInjector.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration.Dynamic; import mockit.Expectations; import mockit.Mocked; public class TestRestServletInjector { Environment environment = Mockito.mock(Environment.class); @Test public void testDefaultInjectEmptyUrlPattern(@Mocked ServletContext servletContext, @Mocked Dynamic dynamic) { new Expectations(ServletConfig.class) { { ServletConfig.getServletUrlPattern(environment); result = null; } }; Assertions.assertNull(RestServletInjector.defaultInject(servletContext, environment)); } @Test public void testDefaultInjectNotListen(@Mocked ServletContext servletContext, @Mocked Dynamic dynamic) throws UnknownHostException, IOException { try (ServerSocket ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"))) { int port = ss.getLocalPort(); new Expectations(ServletConfig.class) { { ServletConfig.getServletUrlPattern(environment); result = "/*"; ServletConfig.getLocalServerAddress(environment); result = "127.0.0.1:" + port; } }; } Assertions.assertNull(RestServletInjector.defaultInject(servletContext, environment)); } @Test public void testDefaultInjectListen(@Mocked ServletContext servletContext, @Mocked Dynamic dynamic) throws UnknownHostException, IOException { try (ServerSocket ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"))) { int port = ss.getLocalPort(); new Expectations(ServletConfig.class) { { ServletConfig.getServletUrlPattern(environment); result = "/rest/*"; ServletConfig.getLocalServerAddress(environment); result = "127.0.0.1:" + port; } }; Assertions.assertEquals(dynamic, RestServletInjector.defaultInject(servletContext, environment)); } } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; import java.io.IOException; import java.net.ServerSocket; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.impl.SysProps; import mockit.Expectations; public class TestServletRestTransport { ServletRestTransport transport; Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")) .thenReturn(""); Mockito.when(environment.getProperty("servicecomb.rest.publishPort", int.class, 0)) .thenReturn(0); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); transport = new ServletRestTransport(); transport.setEnvironment(environment); } @After public void tearDown() { ClassLoaderScopeContext.clearClassLoaderScopeProperty(); } @Test public void testInitNotPublish() { new Expectations(ServletConfig.class) { { ServletConfig.getLocalServerAddress(environment); result = null; } }; Assertions.assertTrue(transport.init()); Assertions.assertNull(transport.getPublishEndpoint()); } @Test public void testInitPublishNoUrlPrefix() { new Expectations(ServletConfig.class) { { ServletConfig.getLocalServerAddress(environment); result = "1.1.1.1:1234"; } }; Assertions.assertTrue(transport.init()); Assertions.assertEquals("rest://1.1.1.1:1234", transport.getPublishEndpoint().getEndpoint()); } @Test public void testInitPublishWithUrlPrefix() { new Expectations(ServletConfig.class) { { ServletConfig.getLocalServerAddress(environment); result = "1.1.1.1:1234"; } }; ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, "/root"); Assertions.assertTrue(transport.init()); Assertions.assertEquals("rest://1.1.1.1:1234?urlPrefix=%2Froot", transport.getPublishEndpoint().getEndpoint()); } @Test public void testGetOrder() { ServletRestTransport transport = new ServletRestTransport(); Assertions.assertEquals(0, transport.getOrder()); } @Test public void testCanInitNullAddress() throws IOException { new Expectations(ServletConfig.class) { { ServletConfig.getLocalServerAddress(environment); result = null; } }; ServletRestTransport transport = new ServletRestTransport(); transport.setEnvironment(environment); Assertions.assertTrue(transport.canInit()); } @Test public void testCanInitListened() throws IOException { ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort(); new Expectations(ServletConfig.class) { { ServletConfig.getLocalServerAddress(environment); result = "0.0.0.0:" + port; } }; ServletRestTransport transport = new ServletRestTransport(); transport.setEnvironment(environment); Assertions.assertTrue(transport.canInit()); ss.close(); } @Test public void testCanInitNotListened() throws IOException { ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort(); ss.close(); new Expectations(ServletConfig.class) { { ServletConfig.getLocalServerAddress(environment); result = "0.0.0.0:" + port; } }; ServletRestTransport transport = new ServletRestTransport(); transport.setEnvironment(environment); Assertions.assertFalse(transport.canInit()); } } ================================================ FILE: transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletUtils.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.servlet; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; import jakarta.servlet.ServletRegistration.Dynamic; import jakarta.servlet.http.HttpServlet; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestServletUtils { Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { Mockito.when(environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR)) .thenReturn(RestConst.UPLOAD_DEFAULT_DIR); Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) .thenReturn(0); LegacyPropertyFactory.setEnvironment(environment); } @Test public void testCheckUrlPatternNormal() { ServletUtils.checkUrlPattern("/*"); ServletUtils.checkUrlPattern("/abc/*"); ServletUtils.checkUrlPattern("/abc/def/*"); // normal, must not throw exception, no need to check } @Test public void testCheckUrlPatternMiddleWideChar() { try { ServletUtils.checkUrlPattern("/abc/*def"); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); } } @Test public void testCheckUrlPatternNoWideChar() { try { ServletUtils.checkUrlPattern("/abcdef"); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); } } @Test public void testCheckUrlPatternNotStartWithSlash() { try { ServletUtils.checkUrlPattern("abcdef/*"); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); } } @Test public void testFilterUrlPatternsNormal() { String urlPattern = "/r1/*"; Collection urlPatterns = Arrays.asList(urlPattern); String[] result = ServletUtils.filterUrlPatterns(urlPatterns); MatcherAssert.assertThat(result, Matchers.arrayContaining("/r1/*")); result = ServletUtils.filterUrlPatterns(urlPattern); MatcherAssert.assertThat(result, Matchers.arrayContaining("/r1/*")); } @Test public void testFilterUrlPatternsEmpty() { Collection urlPatterns = Arrays.asList(" ", "\t"); String[] result = ServletUtils.filterUrlPatterns(urlPatterns); MatcherAssert.assertThat(result, Matchers.emptyArray()); } @Test public void testFilterUrlPatternsInvalid() { Collection urlPatterns = Arrays.asList("/abc"); try { ServletUtils.filterUrlPatterns(urlPatterns); Assertions.fail("must throw exception"); } catch (ServiceCombException e) { Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); } } @Test public void testcollectUrlPatternsNoRestServlet(@Mocked ServletContext servletContext, @Mocked ServletRegistration servletRegistration) { new Expectations() { { servletRegistration.getClassName(); result = "test"; servletContext.getServletRegistrations(); result = Collections.singletonMap("test", servletRegistration); } }; String[] result = ServletUtils.collectUrlPatterns(servletContext, RestServlet.class); MatcherAssert.assertThat(result, Matchers.emptyArray()); } @Test public void testcollectUrlPatternsNormalMapping(@Mocked ServletContext servletContext, @Mocked ServletRegistration r1, @Mocked ServletRegistration r2) { Map servletRegistrationMap = new LinkedHashMap<>(); servletRegistrationMap.put("r1", r1); servletRegistrationMap.put("r2", r2); new Expectations() { { r1.getClassName(); result = RestServlet.class.getName(); r1.getMappings(); result = Arrays.asList("/r1/*", "/r1/1/*"); r2.getClassName(); result = RestServlet.class.getName(); servletContext.getServletRegistrations(); result = servletRegistrationMap; } }; String[] result = ServletUtils.collectUrlPatterns(servletContext, RestServlet.class); MatcherAssert.assertThat(result, Matchers.arrayContaining("/r1/*", "/r1/1/*")); } @Test public void testSaveUrlPrefixNull(@Mocked ServletContext servletContext) { ClassLoaderScopeContext.clearClassLoaderScopeProperty(); ServletUtils.saveUrlPrefix(servletContext); Assertions.assertNull(ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX)); ClassLoaderScopeContext.clearClassLoaderScopeProperty(); } @Test public void testSaveUrlPrefixNormal(@Mocked ServletContext servletContext, @Mocked ServletRegistration servletRegistration) { ClassLoaderScopeContext.clearClassLoaderScopeProperty(); new Expectations() { { servletContext.getContextPath(); result = "/root"; servletRegistration.getClassName(); result = RestServlet.class.getName(); servletRegistration.getMappings(); result = Arrays.asList("/rest/*"); servletContext.getServletRegistrations(); result = Collections.singletonMap("test", servletRegistration); } }; ServletUtils.saveUrlPrefix(servletContext); MatcherAssert.assertThat(ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX), Matchers.is("/root/rest")); ClassLoaderScopeContext.clearClassLoaderScopeProperty(); } @Test public void createUploadDir_relative(@Mocked ServletContext servletContext) throws IOException { File tempDir = Files.createTempDirectory("temp").toFile(); new Expectations() { { servletContext.getAttribute(ServletContext.TEMPDIR); result = tempDir; } }; File expectDir = new File(tempDir, "upload"); Assertions.assertFalse(expectDir.exists()); File dir = ServletUtils.createUploadDir(servletContext, "upload"); Assertions.assertTrue(expectDir.exists()); Assertions.assertEquals(expectDir.getAbsolutePath(), dir.getAbsolutePath()); dir.delete(); Assertions.assertFalse(expectDir.exists()); tempDir.delete(); Assertions.assertFalse(tempDir.exists()); } @Test public void createUploadDir_absolute(@Mocked ServletContext servletContext) throws IOException { File tempDir = Files.createTempDirectory("temp").toFile(); File expectDir = new File(tempDir, "upload"); Assertions.assertFalse(expectDir.exists()); File dir = ServletUtils.createUploadDir(servletContext, expectDir.getAbsolutePath()); Assertions.assertTrue(expectDir.exists()); Assertions.assertEquals(expectDir.getAbsolutePath(), dir.getAbsolutePath()); dir.delete(); Assertions.assertFalse(expectDir.exists()); tempDir.delete(); Assertions.assertFalse(tempDir.exists()); } @Test public void setServletParameters_notSupportUpload() { // not support upload will not set parameters to servlet, so servletContext is null will not throw exception ServletUtils.setServletParameters(null, environment); } @Test public void setServletParameters_supportUpload(@Mocked ServletContext servletContext, @Mocked Dynamic d1, @Mocked ServletRegistration d2) throws IOException { Map servletRegistrations = new HashMap<>(); servletRegistrations.put("d1", d1); servletRegistrations.put("d2", d2); new Expectations() { { servletContext.getServletRegistrations(); result = servletRegistrations; d1.getClassName(); result = RestServlet.class.getName(); d2.getClassName(); result = HttpServlet.class.getName(); } }; List multipartConfigs = new ArrayList<>(); new MockUp(d1) { @Mock void setMultipartConfig(MultipartConfigElement multipartConfig) { multipartConfigs.add(multipartConfig); } }; File tempDir = Files.createTempDirectory("temp").toFile(); File uploadDir = new File(tempDir, "upload"); Mockito.when(environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR)) .thenReturn(uploadDir.getAbsolutePath()); ServletUtils.setServletParameters(servletContext, environment); Assertions.assertEquals(1, multipartConfigs.size()); MultipartConfigElement multipartConfigElement = multipartConfigs.get(0); Assertions.assertEquals(uploadDir.getAbsolutePath(), multipartConfigElement.getLocation()); Assertions.assertEquals(-1, multipartConfigElement.getMaxFileSize()); Assertions.assertEquals(-1, multipartConfigElement.getMaxRequestSize()); Assertions.assertEquals(0, multipartConfigElement.getFileSizeThreshold()); uploadDir.delete(); tempDir.delete(); Assertions.assertFalse(tempDir.exists()); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/pom.xml ================================================ 4.0.0 org.apache.servicecomb transport-rest 3.4.0-SNAPSHOT transport-rest-vertx Java Chassis::Transports::Rest::Vertx org.apache.servicecomb transport-rest-client io.vertx vertx-codegen provided org.apache.servicecomb registry-service-center test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.apache.servicecomb foundation-test-scaffolding org.apache.servicecomb common-access-log org.jmockit jmockit test ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.common.rest.UploadConfig; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.ext.web.handler.BodyHandler; public abstract class AbstractVertxHttpDispatcher implements VertxHttpDispatcher { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractVertxHttpDispatcher.class); protected BodyHandler createBodyHandler() { RestBodyHandler bodyHandler = new RestBodyHandler(); UploadConfig uploadConfig = new UploadConfig(LegacyPropertyFactory.getEnvironment()); bodyHandler.setUploadsDirectory(uploadConfig.getLocation()); bodyHandler.setDeleteUploadedFilesOnEnd(true); bodyHandler.setBodyLimit(uploadConfig.getMaxSize()); if (uploadConfig.toMultipartConfigElement() != null) { LOGGER.info("set uploads directory to \"{}\".", uploadConfig.getLocation()); } return bodyHandler; } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/GlobalRestFailureHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; public interface GlobalRestFailureHandler extends Handler, SPIOrder { } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/HttpServerExceptionHandler.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import io.vertx.core.Handler; import org.apache.servicecomb.foundation.common.utils.SPIOrder; public interface HttpServerExceptionHandler extends Handler, SPIOrder { } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java ================================================ /* * Copyright 2014 Red Hat, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. * */ /* * Froked from https://github.com/vert-x3/vertx-web/blob/master/vertx-web/src/main/java/io/vertx/ext/web/handler/impl/BodyHandlerImpl.java * */ package org.apache.servicecomb.transport.rest.vertx; import java.io.File; import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import com.google.common.annotations.VisibleForTesting; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.http.HttpHeaderValues; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.FileSystem; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.core.http.HttpVersion; import io.vertx.ext.web.FileUpload; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.impl.FileUploadImpl; import io.vertx.ext.web.impl.RoutingContextInternal; /** * copy from io.vertx.ext.web.handler.impl.BodyHandlerImpl * and modified. * * allowed to disable fileupload by setUploadsDirectory(null) */ public class RestBodyHandler implements BodyHandler { private long bodyLimit = DEFAULT_BODY_LIMIT; private boolean handleFileUploads; private String uploadsDir; private boolean mergeFormAttributes = DEFAULT_MERGE_FORM_ATTRIBUTES; private boolean deleteUploadedFilesOnEnd = DEFAULT_DELETE_UPLOADED_FILES_ON_END; private boolean isPreallocateBodyBuffer = DEFAULT_PREALLOCATE_BODY_BUFFER; private static final int DEFAULT_INITIAL_BODY_BUFFER_SIZE = 1024; //bytes public static final String BYPASS_BODY_HANDLER = "__bypass_body_handler"; public RestBodyHandler() { this(true, DEFAULT_UPLOADS_DIRECTORY); } public RestBodyHandler(boolean handleFileUploads) { this(handleFileUploads, DEFAULT_UPLOADS_DIRECTORY); } public RestBodyHandler(String uploadDirectory) { this(true, uploadDirectory); } private RestBodyHandler(boolean handleFileUploads, String uploadDirectory) { this.handleFileUploads = handleFileUploads; setUploadsDirectory(uploadDirectory); } @VisibleForTesting boolean isDeleteUploadedFilesOnEnd() { return deleteUploadedFilesOnEnd; } @Override public void handle(RoutingContext context) { final HttpServerRequest request = context.request(); final HttpServerResponse response = context.response(); if (request.headers().contains(HttpHeaders.UPGRADE, HttpHeaders.WEBSOCKET, true)) { context.next(); return; } Boolean bypass = context.get(BYPASS_BODY_HANDLER); if (Boolean.TRUE.equals(bypass)) { context.next(); return; } // we need to keep state since we can be called again on reroute if (!((RoutingContextInternal) context).seenHandler(RoutingContextInternal.BODY_HANDLER)) { ((RoutingContextInternal) context).visitHandler(RoutingContextInternal.BODY_HANDLER); // Check if a request has a request body. // A request with a body __must__ either have `transfer-encoding` // or `content-length` headers set. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 final long parsedContentLength = parseContentLengthHeader(request); // http2 never transmits a `transfer-encoding` as frames are chunks. final boolean hasTransferEncoding = request.version() == HttpVersion.HTTP_2 || request.headers().contains(HttpHeaders.TRANSFER_ENCODING); if (!hasTransferEncoding && parsedContentLength == -1) { // there is no "body", so we can skip this handler context.next(); return; } // before parsing the body we can already discard a bad request just by inspecting the content-length against // the body limit, this will reduce load, on the server by totally skipping parsing the request body if (bodyLimit != -1 && parsedContentLength != -1) { if (parsedContentLength > bodyLimit) { context.fail(413); return; } } // handle expectations // https://httpwg.org/specs/rfc7231.html#header.expect final String expect = request.getHeader(HttpHeaders.EXPECT); if (expect != null) { // requirements validation if (expect.equalsIgnoreCase("100-continue")) { // A server that receives a 100-continue expectation in an HTTP/1.0 request MUST ignore that expectation. if (request.version() != HttpVersion.HTTP_1_0) { // signal the client to continue response.writeContinue(); } } else { // the server cannot meet the expectation, we only know about 100-continue context.fail(417); return; } } final BHandler handler = new BHandler(context, isPreallocateBodyBuffer ? parsedContentLength : -1); boolean ended = request.isEnded(); if (!ended) { request // resume the request (if paused) .handler(handler) .endHandler(handler::end) .resume(); } } else { // on reroute we need to re-merge the form params if that was desired if (mergeFormAttributes && request.isExpectMultipart()) { request.params().addAll(request.formAttributes()); } context.next(); } } @Override public BodyHandler setHandleFileUploads(boolean handleFileUploads) { this.handleFileUploads = handleFileUploads; return this; } @Override public BodyHandler setBodyLimit(long bodyLimit) { this.bodyLimit = bodyLimit; return this; } @Override public BodyHandler setUploadsDirectory(String uploadsDirectory) { this.uploadsDir = uploadsDirectory; return this; } @Override public BodyHandler setMergeFormAttributes(boolean mergeFormAttributes) { this.mergeFormAttributes = mergeFormAttributes; return this; } @Override public BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd) { this.deleteUploadedFilesOnEnd = deleteUploadedFilesOnEnd; return this; } @Override public BodyHandler setPreallocateBodyBuffer(boolean isPreallocateBodyBuffer) { this.isPreallocateBodyBuffer = isPreallocateBodyBuffer; return this; } private long parseContentLengthHeader(HttpServerRequest request) { String contentLength = request.getHeader(HttpHeaders.CONTENT_LENGTH); if (contentLength == null || contentLength.isEmpty()) { return -1; } try { long parsedContentLength = Long.parseLong(contentLength); return parsedContentLength < 0 ? -1 : parsedContentLength; } catch (NumberFormatException ex) { return -1; } } private class BHandler implements Handler { private static final int MAX_PREALLOCATED_BODY_BUFFER_BYTES = 65535; final RoutingContext context; final long contentLength; Buffer body; volatile boolean failed; final AtomicInteger uploadCount = new AtomicInteger(); volatile boolean ended; long uploadSize = 0L; final boolean isMultipart; final boolean isUrlEncoded; public BHandler(RoutingContext context, long contentLength) { this.context = context; this.contentLength = contentLength; // the request clearly states that there should // be a body, so we respect the client and ensure // that the body will not be null if (contentLength != -1) { initBodyBuffer(); } List fileUploads = context.fileUploads(); final String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); if (contentType == null) { isMultipart = false; isUrlEncoded = false; } else { final String lowerCaseContentType = contentType.toLowerCase(); isMultipart = lowerCaseContentType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString()); isUrlEncoded = lowerCaseContentType.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()); } if (isMultipart || isUrlEncoded) { context.request().setExpectMultipart(true); if (handleFileUploads) { makeUploadDir(context.vertx().fileSystem()); } context.request().uploadHandler(upload -> { // *** cse begin *** if (uploadsDir == null) { failed = true; CommonExceptionData data = new CommonExceptionData("not support file upload."); context.fail(ExceptionFactory.createProducerException(data)); return; } // *** cse end *** if (bodyLimit != -1 && upload.isSizeAvailable()) { // we can try to abort even before the upload starts long size = uploadSize + upload.size(); if (size > bodyLimit) { failed = true; context.cancelAndCleanupFileUploads(); context.fail(413); return; } } if (handleFileUploads) { // we actually upload to a file with a generated filename uploadCount.incrementAndGet(); String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath(); FileUploadImpl fileUpload = new FileUploadImpl(context.vertx().fileSystem(), uploadedFileName, upload); fileUploads.add(fileUpload); Future fut = upload.streamToFileSystem(uploadedFileName); fut.onComplete(ar -> { if (fut.succeeded()) { uploadEnded(); } else { context.cancelAndCleanupFileUploads(); context.fail(ar.cause()); } }); } }); } context.request().exceptionHandler(t -> { context.cancelAndCleanupFileUploads(); int sc = 200; if (t instanceof DecoderException) { // bad request sc = 400; if (t.getCause() != null) { t = t.getCause(); } } context.fail(sc, t); }); } private void initBodyBuffer() { int initialBodyBufferSize; if (contentLength < 0) { initialBodyBufferSize = DEFAULT_INITIAL_BODY_BUFFER_SIZE; } else if (contentLength > MAX_PREALLOCATED_BODY_BUFFER_BYTES) { initialBodyBufferSize = MAX_PREALLOCATED_BODY_BUFFER_BYTES; } else { initialBodyBufferSize = (int) contentLength; } if (bodyLimit != -1) { initialBodyBufferSize = (int) Math.min(initialBodyBufferSize, bodyLimit); } this.body = Buffer.buffer(initialBodyBufferSize); } private void makeUploadDir(FileSystem fileSystem) { // *** cse begin *** if (uploadsDir == null) { return; } // *** cse end *** if (!fileSystem.existsBlocking(uploadsDir)) { fileSystem.mkdirsBlocking(uploadsDir); } } @Override public void handle(Buffer buff) { if (failed) { return; } uploadSize += buff.length(); if (bodyLimit != -1 && uploadSize > bodyLimit) { failed = true; context.cancelAndCleanupFileUploads(); context.fail(413); } else { // multipart requests will not end up in the request body // url encoded should also not, however jQuery by default // post in urlencoded even if the payload is something else if (!isMultipart /* && !isUrlEncoded */) { if (body == null) { initBodyBuffer(); } body.appendBuffer(buff); } } } void uploadEnded() { int count = uploadCount.decrementAndGet(); // only if parsing is done and count is 0 then all files have been processed if (ended && count == 0) { doEnd(); } } void end(Void v) { // this marks the end of body parsing, calling doEnd should // only be possible from this moment onwards ended = true; // only if parsing is done and count is 0 then all files have been processed if (uploadCount.get() == 0) { doEnd(); } } void doEnd() { if (failed || context.failed()) { context.cancelAndCleanupFileUploads(); return; } if (deleteUploadedFilesOnEnd) { context.addBodyEndHandler(x -> context.cancelAndCleanupFileUploads()); } HttpServerRequest req = context.request(); if (mergeFormAttributes && req.isExpectMultipart()) { req.params().addAll(req.formAttributes()); } ((RoutingContextInternal) context).setBody(body); // release body as it may take lots of memory body = null; context.next(); } } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import java.nio.channels.ClosedChannelException; import java.util.List; import java.util.Set; import org.apache.servicecomb.common.accessLog.AccessLogConfig; import org.apache.servicecomb.common.accessLog.core.element.impl.LocalHostAccessItem; import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; import org.apache.servicecomb.foundation.vertx.metrics.DefaultHttpServerMetrics; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import io.vertx.core.AbstractVerticle; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.http.Http2Settings; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpServerResponse; import io.vertx.core.net.impl.ConnectionBase; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.CorsHandler; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; public class RestServerVerticle extends AbstractVerticle { private static final Logger LOGGER = LoggerFactory.getLogger(RestServerVerticle.class); private static final String SSL_KEY = "rest.provider"; private URIEndpointObject endpointObject; private WebSocketDispatcher webSocketDispatcher; @Override public void init(Vertx vertx, Context context) { super.init(vertx, context); Endpoint endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); this.endpointObject = (URIEndpointObject) endpoint.getAddress(); this.webSocketDispatcher = new WebSocketDispatcher(endpoint); } @Override public void start(Promise startPromise) throws Exception { try { super.start(); // 如果本地未配置地址,则表示不必监听,只需要作为客户端使用即可 if (endpointObject == null) { LOGGER.warn("rest listen address is not configured, will not start."); startPromise.complete(); return; } Router mainRouter = Router.router(vertx); mountAccessLogHandler(mainRouter); mountCorsHandler(mainRouter); initDispatcher(mainRouter); mountGlobalRestFailureHandler(mainRouter); HttpServer httpServer = createHttpServer(); httpServer.requestHandler(httpServerRequest -> { if (this.endpointObject.isWebsocketEnabled()) { String path = LegacyPropertyFactory.getStringProperty("servicecomb.rest.server.websocket-prefix"); if (httpServerRequest.path().startsWith(path)) { httpServerRequest.toWebSocket().onComplete(w -> webSocketDispatcher.onRequest(w), e -> LOGGER.error("WebSocket error.", e)); return; } } mainRouter.handle(httpServerRequest); }); httpServer.connectionHandler(connection -> { DefaultHttpServerMetrics serverMetrics = (DefaultHttpServerMetrics) ((ConnectionBase) connection).metrics(); DefaultServerEndpointMetric endpointMetric = serverMetrics.getEndpointMetric(); long connectedCount = endpointMetric.getCurrentConnectionCount(); int connectionLimit = LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.connection-limit", Integer.MAX_VALUE); if (connectedCount > connectionLimit) { connection.close(); endpointMetric.onRejectByConnectionLimit(); } }); List httpServerExceptionHandlers = SPIServiceUtils.getAllService(HttpServerExceptionHandler.class); httpServer.exceptionHandler(e -> { if (e instanceof ClosedChannelException) { // This is quite normal in between browser and edge, so do not print out. LOGGER.debug("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e)); } else { LOGGER.error("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e)); } httpServerExceptionHandlers.forEach(httpServerExceptionHandler -> httpServerExceptionHandler.handle(e)); }); startListen(httpServer, startPromise); } catch (Throwable e) { // vert.x got some states that not print error and execute call back in VertexUtils.blockDeploy, we add a log our self. LOGGER.error("", e); throw e; } } @VisibleForTesting void mountGlobalRestFailureHandler(Router mainRouter) { GlobalRestFailureHandler globalRestFailureHandler = SPIServiceUtils.getPriorityHighestService(GlobalRestFailureHandler.class); Handler failureHandler = null == globalRestFailureHandler ? ctx -> { if (ctx.response().closed() || ctx.response().ended()) { // response has been sent, do nothing LOGGER.error("get a failure with closed response", ctx.failure()); return; } HttpServerResponse response = ctx.response(); if (ctx.failure() instanceof InvocationException exception) { // ServiceComb defined exception response.setStatusCode(exception.getStatusCode()); response.setStatusMessage(exception.getReasonPhrase()); response.end(exception.getErrorData().toString()); return; } LOGGER.error("unexpected failure happened", ctx.failure()); try { // unknown exception CommonExceptionData unknownError = new CommonExceptionData("unknown error"); ctx.response().setStatusCode(500).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) .end(RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(unknownError)); } catch (Exception e) { LOGGER.error("failed to send error response!", e); } } : globalRestFailureHandler; mainRouter.route() // this handler does nothing, just ensure the failure handler can catch exception .handler(RoutingContext::next) .failureHandler(failureHandler); } private void mountAccessLogHandler(Router mainRouter) { if (!AccessLogConfig.INSTANCE.isServerLogEnabled()) { return; } LOGGER.info("access log enabled, pattern = {}", AccessLogConfig.INSTANCE.getServerLogPattern()); mainRouter.route().handler(context -> { ServerAccessLogEvent accessLogEvent = new ServerAccessLogEvent() .setRoutingContext(context) .setMilliStartTime(System.currentTimeMillis()) .setLocalAddress(LocalHostAccessItem.getLocalAddress(context)); context.response().endHandler(event -> EventManager.post(accessLogEvent.setMilliEndTime(System.currentTimeMillis()))); context.next(); }); } /** * Support CORS */ void mountCorsHandler(Router mainRouter) { if (!TransportConfig.isCorsEnabled()) { return; } CorsHandler corsHandler = getCorsHandler(); // Access-Control-Allow-Credentials corsHandler.allowCredentials(TransportConfig.isCorsAllowCredentials()); // Access-Control-Allow-Headers corsHandler.allowedHeaders(TransportConfig.getCorsAllowedHeaders()); // Access-Control-Allow-Methods Set allowedMethods = TransportConfig.getCorsAllowedMethods(); for (String method : allowedMethods) { corsHandler.allowedMethod(HttpMethod.valueOf(method)); } // Access-Control-Expose-Headers corsHandler.exposedHeaders(TransportConfig.getCorsExposedHeaders()); // Access-Control-Max-Age int maxAge = TransportConfig.getCorsMaxAge(); if (maxAge >= 0) { corsHandler.maxAgeSeconds(maxAge); } LOGGER.info("mount CorsHandler"); mainRouter.route().handler(corsHandler); } private CorsHandler getCorsHandler() { CorsHandler handler = CorsHandler.create(); String[] origin = TransportConfig.getCorsAllowedOrigin(); if (origin == null) { handler.addOrigin("*"); } else { for (String item : origin) { handler.addOrigin(item); } } return handler; } private void initDispatcher(Router mainRouter) { List dispatchers = SPIServiceUtils.getOrLoadSortedService(VertxHttpDispatcher.class); for (VertxHttpDispatcher dispatcher : dispatchers) { if (dispatcher.enabled()) { dispatcher.init(mainRouter); } } } private void startListen(HttpServer server, Promise startPromise) { Future result = server.listen(endpointObject.getPort(), endpointObject.getHostOrIp()); result.onComplete((s, f) -> { if (f == null) { LOGGER.info("rest listen success. address={}:{}", endpointObject.getHostOrIp(), s.actualPort()); startPromise.complete(); return; } String msg = String.format("rest listen failed, address=%s:%d", endpointObject.getHostOrIp(), endpointObject.getPort()); LOGGER.error(msg, f); startPromise.fail(f); }); } private HttpServer createHttpServer() { HttpServerOptions serverOptions = createDefaultHttpServerOptions(); return vertx.createHttpServer(serverOptions); } private HttpServerOptions createDefaultHttpServerOptions() { HttpServerOptions serverOptions = new HttpServerOptions(); serverOptions.setCompressionSupported(TransportConfig.getCompressed()); serverOptions.setMaxHeaderSize(TransportConfig.getMaxHeaderSize()); serverOptions.setMaxFormAttributeSize(TransportConfig.getMaxFormAttributeSize()); serverOptions.setMaxFormFields(TransportConfig.getMaxFormFields()); serverOptions.setMaxFormBufferedBytes(TransportConfig.getMaxFormBufferedBytes()); serverOptions.setCompressionLevel(TransportConfig.getCompressionLevel()); serverOptions.setMaxChunkSize(TransportConfig.getMaxChunkSize()); serverOptions.setDecompressionSupported(TransportConfig.getDecompressionSupported()); serverOptions.setDecoderInitialBufferSize(TransportConfig.getDecoderInitialBufferSize()); serverOptions.setMaxInitialLineLength(TransportConfig.getMaxInitialLineLength()); if (endpointObject.isHttp2Enabled()) { serverOptions.setUseAlpn(TransportConfig.getUseAlpn()) .setHttp2ConnectionWindowSize(TransportConfig.getHttp2ConnectionWindowSize()) .setIdleTimeout(TransportConfig.getHttp2ConnectionIdleTimeoutInSeconds()) .setHttp2RstFloodMaxRstFramePerWindow(TransportConfig.getHttp2RstFloodMaxRstFramePerWindow()) .setInitialSettings(new Http2Settings().setPushEnabled(TransportConfig.getPushEnabled()) .setMaxConcurrentStreams(TransportConfig.getMaxConcurrentStreams()) .setHeaderTableSize(TransportConfig.getHttp2HeaderTableSize()) .setInitialWindowSize(TransportConfig.getInitialWindowSize()) .setMaxFrameSize(TransportConfig.getMaxFrameSize()) .setMaxHeaderListSize(TransportConfig.getMaxHeaderListSize()) ); } else { serverOptions.setIdleTimeout(TransportConfig.getConnectionIdleTimeoutInSeconds()); } if (endpointObject.isSslEnabled()) { SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(SSL_KEY, LegacyPropertyFactory.getEnvironment()); SSLOption sslOption; if (factory == null) { sslOption = SSLOption.build(SSL_KEY, LegacyPropertyFactory.getEnvironment()); } else { sslOption = factory.createSSLOption(); } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); VertxTLSBuilder.buildNetServerOptions(sslOption, sslCustom, serverOptions); } return serverOptions; } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import java.util.HashSet; import java.util.Set; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.transport.common.TransportConfigUtils; import io.vertx.core.Verticle; import io.vertx.core.http.Http2Settings; import io.vertx.core.http.HttpServerOptions; public final class TransportConfig { public static final int DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND = 180; public static final boolean DEFAULT_SERVER_COMPRESSION_SUPPORT = false; // 32K public static final int DEFAULT_SERVER_MAX_HEADER_SIZE = 32 * 1024; public static final String SERVICECOMB_CORS_CONFIG_BASE = "servicecomb.cors"; private static Class restServerVerticle = RestServerVerticle.class; private TransportConfig() { } public static Class getRestServerVerticle() { return restServerVerticle; } public static void setRestServerVerticle(Class restServerVerticle) { TransportConfig.restServerVerticle = restServerVerticle; } public static String getAddress() { return LegacyPropertyFactory.getStringProperty("servicecomb.rest.address", null); } public static int getMaxFormAttributeSize() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormAttributeSize", HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); } public static int getMaxFormFields() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormFields", HttpServerOptions.DEFAULT_MAX_FORM_FIELDS); } public static int getMaxFormBufferedBytes() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormBufferedBytes", HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE); } public static int getCompressionLevel() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.compressionLevel", HttpServerOptions.DEFAULT_COMPRESSION_LEVEL); } public static int getMaxChunkSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.maxChunkSize", HttpServerOptions.DEFAULT_MAX_CHUNK_SIZE); } public static int getDecoderInitialBufferSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.decoderInitialBufferSize", HttpServerOptions.DEFAULT_DECODER_INITIAL_BUFFER_SIZE); } public static int getHttp2ConnectionWindowSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.http2.connectionWindowSize", HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE); } public static int getHttp2RstFloodMaxRstFramePerWindow() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.http2.rstFloodMaxRstFramePerWindow", HttpServerOptions.DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW); } public static int getThreadCount() { return TransportConfigUtils.readVerticleCount( "servicecomb.rest.server.verticle-count", "servicecomb.rest.server.thread-count"); } public static int getConnectionIdleTimeoutInSeconds() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.connection.idleTimeoutInSeconds", DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); } public static int getHttp2ConnectionIdleTimeoutInSeconds() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.http2.connection.idleTimeoutInSeconds", DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); } public static boolean getCompressed() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.server.compression", DEFAULT_SERVER_COMPRESSION_SUPPORT); } public static boolean getDecompressionSupported() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.server.decompressionSupported", HttpServerOptions.DEFAULT_DECOMPRESSION_SUPPORTED); } public static long getMaxConcurrentStreams() { return LegacyPropertyFactory .getLongProperty("servicecomb.rest.server.http2.concurrentStreams", HttpServerOptions.DEFAULT_INITIAL_SETTINGS_MAX_CONCURRENT_STREAMS); } public static long getHttp2HeaderTableSize() { return LegacyPropertyFactory .getLongProperty("servicecomb.rest.server.http2.HeaderTableSize", Http2Settings.DEFAULT_HEADER_TABLE_SIZE); } public static boolean getPushEnabled() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.server.http2.pushEnabled", Http2Settings.DEFAULT_ENABLE_PUSH); } public static int getInitialWindowSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.http2.initialWindowSize", Http2Settings.DEFAULT_INITIAL_WINDOW_SIZE); } public static int getMaxFrameSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.http2.maxFrameSize", Http2Settings.DEFAULT_MAX_FRAME_SIZE); } public static int getMaxHeaderListSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.http2.maxHeaderListSize", Http2Settings.DEFAULT_MAX_HEADER_LIST_SIZE); } public static boolean getUseAlpn() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.server.http2.useAlpnEnabled", true); } public static int getMaxHeaderSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.maxHeaderSize", DEFAULT_SERVER_MAX_HEADER_SIZE); } public static boolean isCorsEnabled() { return LegacyPropertyFactory .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".enabled", false); } public static String[] getCorsAllowedOrigin() { return LegacyPropertyFactory .getProperty(SERVICECOMB_CORS_CONFIG_BASE + ".origin", String[].class); } public static boolean isCorsAllowCredentials() { return LegacyPropertyFactory .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowCredentials", false); } public static Set getCorsAllowedHeaders() { String allowedHeaders = LegacyPropertyFactory .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowedHeader"); return convertToSet(allowedHeaders); } public static Set getCorsAllowedMethods() { String allowedMethods = LegacyPropertyFactory .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowedMethod"); return convertToSet(allowedMethods); } public static Set getCorsExposedHeaders() { String exposedHeaders = LegacyPropertyFactory .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".exposedHeader"); return convertToSet(exposedHeaders); } public static int getCorsMaxAge() { return LegacyPropertyFactory .getIntProperty(SERVICECOMB_CORS_CONFIG_BASE + ".maxAge", -1); } private static Set convertToSet(String setString) { Set resultSet = new HashSet<>(); if (!StringUtils.isEmpty(setString)) { String[] arrString = setString.split(","); Stream.of(arrString).map(String::trim).filter(str -> !StringUtils.isEmpty(str)) .forEach(resultSet::add); } return resultSet; } public static int getMaxInitialLineLength() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.maxInitialLineLength", HttpServerOptions.DEFAULT_MAX_INITIAL_LINE_LENGTH); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.foundation.common.utils.SPIOrder; import io.vertx.ext.web.Router; public interface VertxHttpDispatcher extends SPIOrder, SPIEnabled { @Override default boolean enabled() { return true; } void init(Router router); } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; import org.apache.servicecomb.common.rest.RestVertxProducerInvocationCreator; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status.Family; public class VertxRestDispatcher extends AbstractVertxHttpDispatcher { private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestDispatcher.class); private static final String KEY_ORDER = "servicecomb.http.dispatcher.rest.order"; private static final String KEY_ENABLED = "servicecomb.http.dispatcher.rest.enabled"; public static final String KEY_PATTERN = "servicecomb.http.dispatcher.rest.pattern"; private Transport transport; private MicroserviceMeta microserviceMeta; @Override public int getOrder() { return LegacyPropertyFactory.getIntProperty(KEY_ORDER, Integer.MAX_VALUE); } @Override public boolean enabled() { return LegacyPropertyFactory.getBooleanProperty(KEY_ENABLED, true); } @Override public void init(Router router) { // cookies handler are enabled by default start from 3.8.3 String pattern = LegacyPropertyFactory.getStringProperty(KEY_PATTERN); if (pattern == null) { router.route().handler(createBodyHandler()); router.route().failureHandler(this::failureHandler).handler(this::onRequest); } else { router.routeWithRegex(pattern).handler(createBodyHandler()); router.routeWithRegex(pattern).failureHandler(this::failureHandler).handler(this::onRequest); } } protected void failureHandler(RoutingContext context) { LOGGER.error("http server failed.", context.failure()); Throwable e = context.failure(); if (e instanceof ErrorDataDecoderException) { Throwable cause = e.getCause(); if (cause instanceof InvocationException) { e = cause; } } // only when unexpected exception happens, it will run into here. // the connection should be closed. handleFailureAndClose(context, e); } /** * Try to find out the failure information and send it in response. */ private void handleFailureAndClose(RoutingContext context, Throwable e) { if (null != e) { // if there exists exception, try to send this exception by RoutingContext sendExceptionByRoutingContext(context, e); return; } // if there is no exception, the response is determined by status code. sendFailureRespDeterminedByStatus(context); } /** * Try to determine response by status code, and send response. */ private void sendFailureRespDeterminedByStatus(RoutingContext context) { Family statusFamily = Family.familyOf(context.statusCode()); if (Family.CLIENT_ERROR.equals(statusFamily) || Family.SERVER_ERROR.equals(statusFamily) || Family.OTHER .equals(statusFamily)) { context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) .setStatusCode(context.statusCode()).end(); } else { // it seems the status code is not set properly context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) .setStatusCode(Status.INTERNAL_SERVER_ERROR.getStatusCode()) .setStatusMessage(Status.INTERNAL_SERVER_ERROR.getReasonPhrase()) .end(wrapResponseBody(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); } } /** * Use routingContext to send failure information in throwable. */ private void sendExceptionByRoutingContext(RoutingContext context, Throwable e) { if (e instanceof InvocationException invocationException) { context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) .setStatusCode(invocationException.getStatusCode()).setStatusMessage(invocationException.getReasonPhrase()) .end(wrapResponseBody(invocationException.getReasonPhrase())); } else { context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) .setStatusCode(Status.INTERNAL_SERVER_ERROR.getStatusCode()) .end(wrapResponseBody(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); } } /** * Consumer will treat the response body as json by default, so it's necessary to wrap response body as Json string * to avoid deserialization error. * * @param message response body * @return response body wrapped as Json string */ String wrapResponseBody(String message) { if (isValidJson(message)) { return message; } JsonObject jsonObject = new JsonObject(); jsonObject.put("message", message); return jsonObject.toString(); } /** * Check if the message is a valid Json string. * @param message the message to be checked. * @return true if message is a valid Json string, otherwise false. */ private boolean isValidJson(String message) { try { new JsonObject(message); } catch (Exception ignored) { return false; } return true; } protected void onRequest(RoutingContext context) { if (transport == null) { transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL); microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); } HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); InvocationCreator creator = new RestVertxProducerInvocationCreator(context, microserviceMeta, transport.getEndpoint(), requestEx, responseEx); new RestProducerInvocationFlow(creator, requestEx, responseEx) .run(); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.vertx.SimpleJsonObject; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.registry.definition.DefinitionConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.DeploymentOptions; import io.vertx.core.VertxOptions; public class VertxRestTransport extends AbstractTransport { private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestTransport.class); @Override public String getName() { return CoreConst.RESTFUL; } @Override public int getOrder() { return -1000; } @Override public boolean canInit() { String pattern = environment.getProperty(VertxRestDispatcher.KEY_PATTERN, String.class); String urlPrefix = null; if (pattern == null || pattern.length() <= 5) { setListenAddressWithoutSchema(TransportConfig.getAddress()); } else { // e.g. "/api/(.*)" -> "/api" urlPrefix = pattern.substring(0, pattern.length() - 5); setListenAddressWithoutSchema(TransportConfig.getAddress(), Collections.singletonMap(DefinitionConst.URL_PREFIX, urlPrefix)); } URIEndpointObject ep = (URIEndpointObject) getEndpoint().getAddress(); if (ep == null) { return true; } if (!NetUtils.canTcpListen(ep.getSocketAddress().getAddress(), ep.getPort())) { LOGGER.warn( "Can not start VertxRestTransport, the port:{} may have been occupied. " + "You can ignore this message if you are using a web container like tomcat.", ep.getPort()); return false; } if (urlPrefix != null) { ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, urlPrefix); } return true; } @Override public boolean init() throws Exception { // 部署transport server DeploymentOptions options = new DeploymentOptions().setInstances(TransportConfig.getThreadCount()); SimpleJsonObject json = new SimpleJsonObject(); json.put(ENDPOINT_KEY, getEndpoint()); options.setConfig(json); options.setWorkerPoolName("pool-worker-transport-rest"); options.setWorkerPoolSize(VertxOptions.DEFAULT_WORKER_POOL_SIZE); prepareBlockResource(); Map result = VertxUtils.blockDeploy(transportVertx, TransportConfig.getRestServerVerticle(), options); if ((boolean) result.get("code")) { return true; } else { throw new IllegalStateException((String) result.get("message")); } } private void prepareBlockResource() { // block deploy will load resources in event loop, but beans auto wire can only be done in main thread List dispatchers = SPIServiceUtils.getOrLoadSortedService(VertxHttpDispatcher.class); BeanUtils.addBeans(VertxHttpDispatcher.class, dispatchers); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.EdgeServerWebSocketInvocationCreator; import org.apache.servicecomb.common.rest.ProviderServerWebSocketInvocationCreator; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import com.google.common.eventbus.Subscribe; import io.vertx.core.http.ServerWebSocket; import jakarta.ws.rs.core.Response.Status; public class WebSocketDispatcher { private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.websocket.mappings"; private final Object LOCK = new Object(); private volatile boolean initialized = false; private Endpoint endpoint; private MicroserviceMeta microserviceMeta; private boolean isEdge; private Map configurations = new HashMap<>(); public WebSocketDispatcher(Endpoint endpoint) { this.endpoint = endpoint; EventManager.register(this); } private void loadConfigurations() { configurations = URLMappedConfigurationLoader.loadConfigurations( LegacyPropertyFactory.getEnvironment(), KEY_MAPPING_PREFIX); } @Subscribe public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(KEY_MAPPING_PREFIX)) { loadConfigurations(); break; } } } protected void onRequest(ServerWebSocket webSocket) { if (!initialized) { synchronized (LOCK) { if (!initialized) { Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.WEBSOCKET); this.microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); this.endpoint = new Endpoint(transport, this.endpoint.getEndpoint()); this.isEdge = TransportConfig.getRestServerVerticle() .getName().equals("org.apache.servicecomb.edge.core.EdgeRestServerVerticle"); if (this.isEdge) { loadConfigurations(); } } initialized = true; } } InvocationCreator creator; if (isEdge) { URLMappedConfigurationItem configurationItem = findConfigurationItem(webSocket.path()); if (configurationItem == null) { throw new InvocationException(Status.NOT_FOUND, new CommonExceptionData( String.format("path %s not found", webSocket.path()))); } String path = Utils.findActualPath(webSocket.path(), configurationItem.getPrefixSegmentCount()); creator = new EdgeServerWebSocketInvocationCreator( configurationItem.getMicroserviceName(), path, endpoint, webSocket); } else { creator = new ProviderServerWebSocketInvocationCreator(microserviceMeta, endpoint, webSocket); } new WebSocketProducerInvocationFlow(creator, webSocket).run(); } private URLMappedConfigurationItem findConfigurationItem(String path) { for (URLMappedConfigurationItem item : configurations.values()) { if (item.getPattern().matcher(path).matches()) { return item; } } return null; } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.http.ServerWebSocket; public class WebSocketProducerInvocationFlow extends ProducerInvocationFlow { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketProducerInvocationFlow.class); private final ServerWebSocket websocket; public WebSocketProducerInvocationFlow(InvocationCreator invocationCreator, ServerWebSocket webSocket) { super(invocationCreator); this.websocket = webSocket; } @Override protected Invocation sendCreateInvocationException(Throwable throwable) { LOGGER.error("Web socket create invocation error.", throwable); websocket.writeTextMessage("Web socket create invocation error " + throwable.getMessage()); websocket.close(); return null; } @Override protected void endResponse(Invocation invocation, Response response) { } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.transport.AbstractTransport; public class WebSocketTransport extends AbstractTransport { @Override public String getName() { return CoreConst.WEBSOCKET; } @Override public int getOrder() { return -500; } @Override public boolean init() throws Exception { return true; } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.transport.rest.vertx.VertxRestTransport org.apache.servicecomb.transport.rest.vertx.WebSocketTransport ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher ================================================ # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # org.apache.servicecomb.transport.rest.vertx.VertxRestDispatcher ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockForRestServerVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.foundation.common.net.IpPort; import org.mockito.Mockito; import io.vertx.core.Future; import io.vertx.core.http.HttpServer; import mockit.Mock; import mockit.MockUp; public class MockForRestServerVerticle { private static final MockForRestServerVerticle instance = new MockForRestServerVerticle(); private MockForRestServerVerticle() { // private constructor for Singleton } public static MockForRestServerVerticle getInstance() { return instance; } public void mockRestServerVerticle() { final HttpServer server = Mockito.mock(HttpServer.class); new MockUp() { @Mock private void startListen(HttpServer server, IpPort ipPort, Future startFuture) { } @Mock private HttpServer createHttpServer(boolean isHttp_2) { return server; } }; } public void mockTransportConfig() { new MockUp() { @Mock public String getAddress() { return "Address"; } }; } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Map; import java.util.Set; import io.vertx.codegen.annotations.Nullable; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerResponse; import io.vertx.core.net.HostAndPort; class MockHttpServerResponse implements HttpServerResponse { boolean responseEnded; Map responseHeader = new HashMap<>(1); int responseStatusCode; String responseStatusMessage; String responseChunk; @Override public HttpServerResponse putHeader(String name, String value) { responseHeader.put(name, value); return this; } @Override public HttpServerResponse setStatusCode(int statusCode) { responseStatusCode = statusCode; return this; } @Override public HttpServerResponse setStatusMessage(String statusMessage) { responseStatusMessage = statusMessage; return this; } @Override public Future end() { responseEnded = true; return Future.succeededFuture(); } @Override public Future end(String chunk) { responseEnded = true; responseChunk = chunk; return Future.succeededFuture(); } @Override public HttpServerResponse exceptionHandler(Handler handler) { return null; } @Override public Future write(Buffer data) { return Future.succeededFuture(); } @Override public HttpServerResponse setWriteQueueMaxSize(int maxSize) { return null; } @Override public boolean writeQueueFull() { return false; } @Override public HttpServerResponse drainHandler(Handler handler) { return null; } @Override public int getStatusCode() { return 0; } @Override public String getStatusMessage() { return null; } @Override public HttpServerResponse setChunked(boolean chunked) { return null; } @Override public boolean isChunked() { return false; } @Override public MultiMap headers() { return null; } @Override public HttpServerResponse putHeader(CharSequence name, CharSequence value) { return null; } @Override public HttpServerResponse putHeader(String name, Iterable values) { return null; } @Override public HttpServerResponse putHeader(CharSequence name, Iterable values) { return null; } @Override public MultiMap trailers() { return null; } @Override public HttpServerResponse putTrailer(String name, String value) { return null; } @Override public HttpServerResponse putTrailer(CharSequence name, CharSequence value) { return null; } @Override public HttpServerResponse putTrailer(String name, Iterable values) { return null; } @Override public HttpServerResponse putTrailer(CharSequence name, Iterable value) { return null; } @Override public HttpServerResponse closeHandler(Handler handler) { return null; } @Override public HttpServerResponse endHandler(Handler handler) { return null; } @Override public Future writeHead() { return null; } @Override public Future write(String chunk, String enc) { return Future.succeededFuture(); } @Override public Future write(String chunk) { return Future.succeededFuture(); } @Override public Future writeContinue() { return null; } @Override public Future writeEarlyHints(MultiMap headers) { return Future.succeededFuture(); } @Override public Future end(String chunk, String enc) { return Future.succeededFuture(); } @Override public Future end(Buffer chunk) { return Future.succeededFuture(); } @Override public Future sendFile(String filename, long offset, long length) { return Future.succeededFuture(); } @Override public Future sendFile(FileChannel channel, long offset, long length) { return null; } @Override public Future sendFile(RandomAccessFile file, long offset, long length) { return null; } @Override public boolean ended() { return false; } @Override public boolean closed() { return false; } @Override public boolean headWritten() { return false; } @Override public HttpServerResponse headersEndHandler(Handler handler) { return null; } @Override public HttpServerResponse bodyEndHandler(Handler handler) { return null; } @Override public long bytesWritten() { return 0; } @Override public int streamId() { return 0; } @Override public Future push(HttpMethod httpMethod, HostAndPort hostAndPort, String s, MultiMap multiMap) { return null; } @Override public Future reset(long code) { return null; } @Override public Future writeCustomFrame(int type, int flags, Buffer payload) { return null; } @Override public HttpServerResponse addCookie(Cookie cookie) { return null; } @Override public @Nullable Cookie removeCookie(String name, boolean invalidate) { return null; } @Override public Set removeCookies(String name, boolean invalidate) { return null; } @Override public @Nullable Cookie removeCookie(String name, String domain, String path, boolean invalidate) { return null; } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestAbstractVertxHttpDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.runners.MethodSorters; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.ext.web.Router; import mockit.Deencapsulation; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestAbstractVertxHttpDispatcher { static class AbstractVertxHttpDispatcherForTest extends AbstractVertxHttpDispatcher { @Override public int getOrder() { return 0; } @Override public void init(Router router) { } } Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty( RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) .thenReturn(0); } @Test public void createBodyHandlerUploadDefault() { Mockito.when(environment.getProperty( "servicecomb.uploads.directory", RestConst.UPLOAD_DEFAULT_DIR)) .thenReturn(RestConst.UPLOAD_DEFAULT_DIR); AbstractVertxHttpDispatcher dispatcher = new AbstractVertxHttpDispatcherForTest(); RestBodyHandler bodyHandler = (RestBodyHandler) dispatcher.createBodyHandler(); Assertions.assertTrue(bodyHandler.isDeleteUploadedFilesOnEnd()); Assertions.assertEquals(RestConst.UPLOAD_DEFAULT_DIR, Deencapsulation.getField(bodyHandler, "uploadsDir")); } @Test public void createBodyHandlerUploadNormal() { Mockito.when(environment.getProperty( "servicecomb.uploads.directory", RestConst.UPLOAD_DEFAULT_DIR)) .thenReturn("/path"); AbstractVertxHttpDispatcher dispatcher = new AbstractVertxHttpDispatcherForTest(); RestBodyHandler bodyHandler = (RestBodyHandler) dispatcher.createBodyHandler(); Assertions.assertTrue(bodyHandler.isDeleteUploadedFilesOnEnd()); Assertions.assertEquals("/path", Deencapsulation.getField(bodyHandler, "uploadsDir")); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import static io.vertx.core.http.HttpServerOptions.DEFAULT_COMPRESSION_LEVEL; import static io.vertx.core.http.HttpServerOptions.DEFAULT_DECODER_INITIAL_BUFFER_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_DECOMPRESSION_SUPPORTED; import static io.vertx.core.http.HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_CHUNK_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_FIELDS; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_INITIAL_LINE_LENGTH; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.CLIENT_LOG_ENABLED; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.CLIENT_LOG_PATTERN; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.DEFAULT_CLIENT_PATTERN; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.DEFAULT_SERVER_PATTERN; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.SERVER_LOG_ENABLED; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.SERVER_LOG_PATTERN; import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.DEFAULT_SERVER_COMPRESSION_SUPPORT; import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND; import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.DEFAULT_SERVER_MAX_HEADER_SIZE; import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.SERVICECOMB_CORS_CONFIG_BASE; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.transport.AbstractTransport; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.Context; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.http.Http2Settings; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerResponse; import io.vertx.core.impl.SysProps; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.CorsHandler; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestRestServerVerticle { private RestServerVerticle instance = null; Promise startPromise = null; Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { Mockito.when(environment.getProperty( "servicecomb.request.timeout", long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) .thenReturn((long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT); Mockito.when(environment.getProperty("servicecomb.rest.client.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.rest.client.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.rest.server.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.rest.server.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.rest.order", int.class, Integer.MAX_VALUE)) .thenReturn(Integer.MAX_VALUE); Mockito.when(environment.getProperty("servicecomb.rest.publishPort", int.class, 0)) .thenReturn(0); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.rest.enabled", boolean.class, true)) .thenReturn(true); Mockito.when(environment.getProperty(SERVICECOMB_CORS_CONFIG_BASE + ".enabled", boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")) .thenReturn(""); Mockito.when(environment.getProperty( RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) .thenReturn(0); Mockito.when(environment.getProperty("servicecomb.rest.server.compression", boolean.class, DEFAULT_SERVER_COMPRESSION_SUPPORT)) .thenReturn(false); Mockito.when(environment.getProperty("servicecomb.rest.server.maxHeaderSize", int.class, DEFAULT_SERVER_MAX_HEADER_SIZE)) .thenReturn(DEFAULT_SERVER_MAX_HEADER_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormAttributeSize", int.class, DEFAULT_MAX_FORM_ATTRIBUTE_SIZE)) .thenReturn(DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.compressionLevel", int.class, DEFAULT_COMPRESSION_LEVEL)) .thenReturn(DEFAULT_COMPRESSION_LEVEL); Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormFields", int.class, DEFAULT_MAX_FORM_FIELDS)) .thenReturn(DEFAULT_MAX_FORM_FIELDS); Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormBufferedBytes", int.class, DEFAULT_MAX_FORM_BUFFERED_SIZE)) .thenReturn(DEFAULT_MAX_FORM_BUFFERED_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.maxChunkSize", int.class, DEFAULT_MAX_CHUNK_SIZE)) .thenReturn(DEFAULT_MAX_CHUNK_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.decompressionSupported", boolean.class, DEFAULT_DECOMPRESSION_SUPPORTED)) .thenReturn(DEFAULT_DECOMPRESSION_SUPPORTED); Mockito.when(environment.getProperty("servicecomb.rest.server.decoderInitialBufferSize", int.class, DEFAULT_DECODER_INITIAL_BUFFER_SIZE)) .thenReturn(DEFAULT_DECODER_INITIAL_BUFFER_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.maxInitialLineLength", int.class, DEFAULT_MAX_INITIAL_LINE_LENGTH)) .thenReturn(DEFAULT_MAX_INITIAL_LINE_LENGTH); Mockito.when(environment.getProperty("servicecomb.rest.server.connection.idleTimeoutInSeconds", int.class, DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND)) .thenReturn(DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); Mockito.when(environment.getProperty("servicecomb.rest.server.http2.useAlpnEnabled", boolean.class, true)) .thenReturn(true); Mockito.when(environment.getProperty("servicecomb.rest.server.http2ConnectionWindowSize", int.class, DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE)) .thenReturn(DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.http2.connection.idleTimeoutInSeconds", int.class, DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND)) .thenReturn(DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); Mockito.when(environment.getProperty("servicecomb.rest.server.http2.pushEnabled", boolean.class, Http2Settings.DEFAULT_ENABLE_PUSH)) .thenReturn(Http2Settings.DEFAULT_ENABLE_PUSH); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); Mockito.when(environment.getProperty(CLIENT_LOG_ENABLED, boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(SERVER_LOG_ENABLED, boolean.class, false)) .thenReturn(false); Mockito.when(environment.getProperty(CLIENT_LOG_PATTERN, String.class, DEFAULT_CLIENT_PATTERN)) .thenReturn(DEFAULT_CLIENT_PATTERN); Mockito.when(environment.getProperty(SERVER_LOG_PATTERN, String.class, DEFAULT_SERVER_PATTERN)) .thenReturn(DEFAULT_CLIENT_PATTERN); LegacyPropertyFactory.setEnvironment(environment); instance = new RestServerVerticle(); startPromise = Promise.promise(); SCBBootstrap.createSCBEngineForTest(environment); } @After public void tearDown() { instance = null; startPromise = null; SCBEngine.getInstance().destroy(); } @Test public void testRestServerVerticleWithRouter(@Mocked Transport transport, @Mocked Vertx vertx, @Mocked Context context, @Mocked JsonObject jsonObject, @Mocked Promise startPromise) throws Exception { URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080"); new Expectations() { { transport.parseAddress("http://127.0.0.1:8080"); result = endpointObject; } }; Endpoint endpoint = new Endpoint(transport, "http://127.0.0.1:8080"); new Expectations() { { context.config(); result = jsonObject; jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); result = endpoint; } }; RestServerVerticle server = new RestServerVerticle(); // process stuff done by Expectations server.init(vertx, context); server.start(startPromise); } @Test public void testRestServerVerticleWithRouterSSL(@Mocked Transport transport, @Mocked Vertx vertx, @Mocked Context context, @Mocked JsonObject jsonObject, @Mocked Promise startPromise) throws Exception { URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080?sslEnabled=true"); new Expectations() { { transport.parseAddress("http://127.0.0.1:8080?sslEnabled=true"); result = endpointObject; } }; Endpoint endpoint = new Endpoint(transport, "http://127.0.0.1:8080?sslEnabled=true"); new Expectations() { { context.config(); result = jsonObject; jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); result = endpoint; } }; RestServerVerticle server = new RestServerVerticle(); // process stuff done by Expectations server.init(vertx, context); server.start(startPromise); } @Test public void testStartFutureAddressEmpty() { boolean status = false; try { instance.start(startPromise); } catch (Exception ex) { status = true; } Assertions.assertFalse(status); } @Test public void testStartFutureAddressNotEmpty() { boolean status = false; MockForRestServerVerticle.getInstance().mockTransportConfig(); MockForRestServerVerticle.getInstance().mockRestServerVerticle(); try { instance.start(startPromise); } catch (Exception ex) { status = true; } Assertions.assertFalse(status); } @Test public void testMountCorsHandler() { Mockito.when(environment.getProperty("servicecomb.cors.enabled", boolean.class, false)) .thenReturn(true); Mockito.when(environment.getProperty("servicecomb.cors.origin", String[].class)) .thenReturn(null); Mockito.when(environment.getProperty("servicecomb.cors.allowedMethod")) .thenReturn("GET,PUT,POST"); Mockito.when(environment.getProperty("servicecomb.cors.allowedHeader")) .thenReturn("abc,def"); Mockito.when(environment.getProperty("servicecomb.cors.exposedHeader")) .thenReturn("abc2,def2"); Mockito.when(environment.getProperty("servicecomb.cors.maxAge", int.class, -1)) .thenReturn(1); Mockito.when(environment.getProperty("servicecomb.cors.allowCredentials", boolean.class, false)) .thenReturn(false); Set methodSet = new HashSet<>(3); methodSet.add(HttpMethod.GET); methodSet.add(HttpMethod.PUT); methodSet.add(HttpMethod.POST); AtomicInteger counter = new AtomicInteger(0); CorsHandler corsHandler = new MockUp() { @Mock CorsHandler allowCredentials(boolean allow) { Assertions.assertFalse(allow); counter.incrementAndGet(); return null; } @Mock CorsHandler allowedHeaders(Set headerNames) { MatcherAssert.assertThat(headerNames, Matchers.containsInAnyOrder("abc", "def")); counter.incrementAndGet(); return null; } @Mock CorsHandler exposedHeaders(Set headerNames) { MatcherAssert.assertThat(headerNames, Matchers.containsInAnyOrder("abc2", "def2")); counter.incrementAndGet(); return null; } @Mock CorsHandler allowedMethod(HttpMethod method) { Assertions.assertTrue(methodSet.contains(method)); counter.incrementAndGet(); methodSet.remove(method); return null; } @Mock CorsHandler maxAgeSeconds(int maxAgeSeconds) { Assertions.assertEquals(1, maxAgeSeconds); counter.incrementAndGet(); return null; } }.getMockInstance(); new MockUp() { @Mock CorsHandler getCorsHandler() { return corsHandler; } }; Router router = Mockito.mock(Router.class); Mockito.when(router.route()).thenReturn(Mockito.mock(Route.class)); RestServerVerticle server = new RestServerVerticle(); server.mountCorsHandler(router); Assertions.assertEquals(7, counter.get()); } @Test public void mountGlobalRestFailureHandler() { Router mainRouter = Mockito.mock(Router.class); Holder> handlerHolder = new Holder<>(); Holder routeHolder = new Holder<>(); Route route = new MockUp() { @Mock Route failureHandler(Handler failureHandler) { handlerHolder.value = failureHandler; return null; } @Mock Route handler(io.vertx.core.Handler requestHandler) { return routeHolder.value; } }.getMockInstance(); routeHolder.value = route; Mockito.when(mainRouter.route()).thenReturn(route); RestServerVerticle restServerVerticle = new RestServerVerticle(); restServerVerticle.mountGlobalRestFailureHandler(mainRouter); Assertions.assertNotNull(handlerHolder.value); RoutingContext routingContext = Mockito.mock(RoutingContext.class); HttpServerResponse response = Mockito.mock(HttpServerResponse.class); Mockito.when(response.setStatusCode(500)).thenReturn(response); Mockito.when(response.putHeader("Content-Type", "application/json")).thenReturn(response); Mockito.when(routingContext.response()).thenReturn(response); handlerHolder.value.handle(routingContext); Mockito.verify(response).end("{\"message\":\"unknown error\"}"); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestDispatcher.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import org.apache.http.HttpHeaders; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; import mockit.Mocked; public class TestVertxRestDispatcher { @Mocked Router mainRouter; @Mocked TransportManager transportManager; VertxRestDispatcher dispatcher; Throwable throwable; Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty( RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) .thenReturn(-1L); Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) .thenReturn(0); dispatcher = new VertxRestDispatcher(); dispatcher.init(mainRouter); SCBBootstrap.createSCBEngineForTest(environment).setTransportManager(transportManager); } @After public void teardown() { SCBEngine.getInstance().destroy(); } @Test public void getOrder() { Mockito.when(environment.getProperty( "servicecomb.http.dispatcher.rest.order", int.class, Integer.MAX_VALUE)) .thenReturn(Integer.MAX_VALUE); Assertions.assertEquals(Integer.MAX_VALUE, dispatcher.getOrder()); } @Test public void failureHandlerWithNoRestProducerInvocationAndInvocationException() { RoutingContext context = Mockito.mock(RoutingContext.class); Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); InvocationException e = new InvocationException(Status.REQUEST_ENTITY_TOO_LARGE, "testMsg"); ErrorDataDecoderException edde = new ErrorDataDecoderException(e); Mockito.when(context.failure()).thenReturn(edde); MockHttpServerResponse response = new MockHttpServerResponse(); Mockito.when(context.response()).thenReturn(response); dispatcher.failureHandler(context); MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode())); MatcherAssert.assertThat(response.responseStatusMessage, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getReasonPhrase())); MatcherAssert.assertThat(response.responseChunk, Matchers.is("{\"message\":\"" + Status.REQUEST_ENTITY_TOO_LARGE.getReasonPhrase() + "\"}")); Assertions.assertTrue(response.responseEnded); } @Test public void failureHandlerWithNoRestProducerInvocationAndOtherException() { RoutingContext context = Mockito.mock(RoutingContext.class); Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); String exceptionMessage = "Internal Server Error"; Exception exception = new Exception(exceptionMessage); Mockito.when(context.failure()).thenReturn(exception); MockHttpServerResponse response = new MockHttpServerResponse(); Mockito.when(context.response()).thenReturn(response); dispatcher.failureHandler(context); MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.INTERNAL_SERVER_ERROR.getStatusCode())); MatcherAssert.assertThat(response.responseChunk, Matchers.is("{\"message\":\"" + exceptionMessage + "\"}")); Assertions.assertTrue(response.responseEnded); } @Test public void failureHandlerWithNoExceptionAndStatusCodeIsSet() { RoutingContext context = Mockito.mock(RoutingContext.class); Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); Mockito.when(context.failure()).thenReturn(null); MockHttpServerResponse response = new MockHttpServerResponse(); Mockito.when(context.response()).thenReturn(response); Mockito.when(context.statusCode()).thenReturn(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode()); dispatcher.failureHandler(context); MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode())); Assertions.assertTrue(response.responseEnded); } @Test public void failureHandlerWithNoExceptionAndStatusCodeIsNotSet() { RoutingContext context = Mockito.mock(RoutingContext.class); Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); Mockito.when(context.failure()).thenReturn(null); MockHttpServerResponse response = new MockHttpServerResponse(); Mockito.when(context.response()).thenReturn(response); Mockito.when(context.statusCode()).thenReturn(Status.OK.getStatusCode()); dispatcher.failureHandler(context); MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.INTERNAL_SERVER_ERROR.getStatusCode())); MatcherAssert.assertThat(response.responseStatusMessage, Matchers.is(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); MatcherAssert.assertThat(response.responseChunk, Matchers.is("{\"message\":\"" + Status.INTERNAL_SERVER_ERROR.getReasonPhrase() + "\"}")); Assertions.assertTrue(response.responseEnded); } @Test public void testWrapResponseBody() { VertxRestDispatcher vertxRestDispatcher = new VertxRestDispatcher(); String message = "abcd"; String bodyString = vertxRestDispatcher.wrapResponseBody(message); Assertions.assertNotNull(bodyString); Assertions.assertEquals("{\"message\":\"abcd\"}", bodyString); message = "\"abcd\""; bodyString = vertxRestDispatcher.wrapResponseBody(message); Assertions.assertNotNull(bodyString); Assertions.assertEquals("{\"message\":\"\\\"abcd\\\"\"}", bodyString); message = ".01ab\"!@#$%^&*()'\\cd"; bodyString = vertxRestDispatcher.wrapResponseBody(message); Assertions.assertNotNull(bodyString); Assertions.assertEquals("{\"message\":\".01ab\\\"!@#$%^&*()'\\\\cd\"}", bodyString); message = new JsonObject().put("key", new JsonObject().put("k2", "value")).toString(); bodyString = vertxRestDispatcher.wrapResponseBody(message); Assertions.assertNotNull(bodyString); Assertions.assertEquals("{\"key\":{\"k2\":\"value\"}}", bodyString); message = "ab\"23\n@!#cd"; bodyString = vertxRestDispatcher.wrapResponseBody(message); Assertions.assertNotNull(bodyString); Assertions.assertEquals("{\"message\":\"ab\\\"23\\n@!#cd\"}", bodyString); message = "ab\"23\r\n@!#cd"; bodyString = vertxRestDispatcher.wrapResponseBody(message); Assertions.assertNotNull(bodyString); Assertions.assertEquals("{\"message\":\"ab\\\"23\\r\\n@!#cd\"}", bodyString); } } ================================================ FILE: transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.servicecomb.transport.rest.vertx; import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; import java.io.IOException; import java.net.ServerSocket; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.core.env.Environment; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.impl.SysProps; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; public class TestVertxRestTransport { private VertxRestTransport instance; Environment environment = Mockito.mock(Environment.class); @Before public void setUp() { Mockito.when(environment.getProperty( "servicecomb.request.timeout", long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) .thenReturn((long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT); Mockito.when(environment.getProperty("servicecomb.rest.client.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.rest.client.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.rest.server.verticle-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.rest.server.thread-count", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.http.dispatcher.rest.order", int.class, Integer.MAX_VALUE)) .thenReturn(Integer.MAX_VALUE); Mockito.when(environment.getProperty("servicecomb.rest.publishPort", int.class, 0)) .thenReturn(0); Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")) .thenReturn(""); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); instance = new VertxRestTransport(); instance.setEnvironment(environment); } @Test public void testGetInstance() { Assertions.assertNotNull(instance); } @Test public void testGetName() { Assertions.assertEquals("rest", instance.getName()); } @Test public void testInit() { boolean status = false; try { new MockUp() { @Mock public Vertx init(VertxOptions vertxOptions) { return null; } @Mock public Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) throws InterruptedException { Map result = new HashMap<>(); result.put("code", true); return result; } }; instance.init(); } catch (Exception e) { e.printStackTrace(); status = true; } Assertions.assertFalse(status); } @Test public void testGetOrder() { VertxRestTransport transport = new VertxRestTransport(); Assertions.assertEquals(-1000, transport.getOrder()); } @Test public void testCanInitNullAddress() throws IOException { new Expectations(TransportConfig.class) { { TransportConfig.getAddress(); result = null; } }; VertxRestTransport transport = new VertxRestTransport(); Environment environment = Mockito.mock(Environment.class); transport.setEnvironment(environment); Assertions.assertTrue(transport.canInit()); } @Test public void testCanInitListened() throws IOException { ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort(); new Expectations(TransportConfig.class) { { TransportConfig.getAddress(); result = "0.0.0.0:" + port; } }; VertxRestTransport transport = new VertxRestTransport(); transport.setEnvironment(environment); Assertions.assertFalse(transport.canInit()); ss.close(); } @Test public void testCanInitNotListened() throws IOException { ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort(); ss.close(); new Expectations(TransportConfig.class) { { TransportConfig.getAddress(); result = "0.0.0.0:" + port; } }; VertxRestTransport transport = new VertxRestTransport(); transport.setEnvironment(environment); Assertions.assertTrue(transport.canInit()); } }